From 2c0c693956c115d5ccd1223e378bf9774626ad55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Apr 2018 08:41:02 +0200 Subject: [PATCH 001/301] OpenGL: Similar to NvOptimusEnablement, use AmdPowerXPressRequestHighPerformance. --- Windows/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Windows/main.cpp b/Windows/main.cpp index cf04660d4e..8d81eae7fa 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -73,6 +73,11 @@ extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 1; } +// Also on AMD PowerExpress: https://community.amd.com/thread/169965 +extern "C" { + __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +} + CDisasm *disasmWindow[MAX_CPUCOUNT] = {0}; CGEDebugger *geDebuggerWindow = 0; CMemoryDlg *memoryWindow[MAX_CPUCOUNT] = {0}; From 687b5f92b162f7206bfba3222b69c0782628980d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 20 Apr 2018 11:01:33 +0200 Subject: [PATCH 002/301] Limit the flip delay in the other direction to try to work around #10763. --- Core/CoreTiming.cpp | 1 + Core/HLE/sceDisplay.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 78ca1d2fb8..5cebf2f3a5 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -585,6 +585,7 @@ void Advance() int cyclesExecuted = slicelength - currentMIPS->downcount; globalTimer += cyclesExecuted; currentMIPS->downcount = slicelength; + VERBOSE_LOG(SCEDISPLAY, "CoreTiming: Event type '%s'", event_types[first->type].name); if (Common::AtomicLoadAcquire(hasTsEvents)) MoveEvents(); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 8c7fd13597..01521d4b34 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -588,6 +588,7 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { static void DoFrameIdleTiming() { PROFILE_THIS_SCOPE("timing"); + if (!FrameTimingThrottled() || !g_Config.bEnableSound || wasPaused) { return; } @@ -713,6 +714,7 @@ void __DisplayFlip(int cyclesLate) { // Check first though, might've just quit / been paused. if (coreState == CORE_RUNNING) { coreState = CORE_NEXTFRAME; + DEBUG_LOG(SCEDISPLAY, "No recent flip - displaying anyway."); gpu->CopyDisplayToOutput(); if (fbReallyDirty) { actualFlips++; @@ -910,6 +912,8 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) // sceDisplaySetFramebuf() isn't supposed to delay threads at all. This is a hack. // So let's only delay when it's more than 1ms. const s64 FLIP_DELAY_CYCLES_MIN = usToCycles(1000); + // Though if we are super early, we also don't want to delay because the game is being silly like in #10763 + const s64 FLIP_DELAY_CYCLES_MAX = usToCycles(16000); // This is slightly less than a full frame at 60hz, 16666 // Some games (like Final Fantasy 4) only call this too much in spurts. // The goal is to fix games where this would result in a consistent overhead. const int FLIP_DELAY_MIN_FLIPS = 30; @@ -919,7 +923,7 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) u64 now = CoreTiming::GetTicks(); s64 cyclesAhead = nextFlipCycles - now; - if (cyclesAhead > FLIP_DELAY_CYCLES_MIN) { + if (cyclesAhead > FLIP_DELAY_CYCLES_MIN && (cyclesAhead < FLIP_DELAY_CYCLES_MAX || PSP_CoreParameter().unthrottle)) { if (lastFlipsTooFrequent >= FLIP_DELAY_MIN_FLIPS && gpuStats.numClears > 0) { delayCycles = cyclesAhead; } else { From 282b81f60112b4b2ce3832c7c3056d8c21b9176c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 22 Apr 2018 10:35:09 +0200 Subject: [PATCH 003/301] Update README.md for 1.6 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index ec7d514b14..8b7a17ed6b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,15 @@ For build instructions and other development tutorials, see [the wiki](https://g For game compatibility, see [community compatibility feedback](http://report.ppsspp.org/games). +What's new in 1.6.0 +------------------- +* OpenGL backend now properly multithreaded, giving a good speed boost. +* Various Vulkan performance improvements and memory allocation fixes. +* Various fixes for app switching on Android +* Bugfixes and some performance improvements in the ARM64 JIT compiler and IR interpreter +* Shader cache enabled for Vulkan +* A few iOS fixes +* Retroarch support What's new in 1.5.4 ------------------- From 8d7f63444ab169f8203e1e81d5b1abc8266fc7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 24 Apr 2018 19:53:35 +0200 Subject: [PATCH 004/301] Improve README for 1.6 --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8b7a17ed6b..72dfbeda3b 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,16 @@ For game compatibility, see [community compatibility feedback](http://report.pps What's new in 1.6.0 ------------------- * OpenGL backend now properly multithreaded, giving a good speed boost. -* Various Vulkan performance improvements and memory allocation fixes. -* Various fixes for app switching on Android +* Various Vulkan performance improvements (like #10911) and memory allocation fixes. +* GPU command interpreter performance improvements (#10658) +* Various fixes for app switching and widgets (#10855) on Android * Bugfixes and some performance improvements in the ARM64 JIT compiler and IR interpreter * Shader cache enabled for Vulkan -* A few iOS fixes +* Multiple iOS fixes, including JIT (#10465) and file browser (#10921). +* Improved compatibility on Mac (#10113) +* Texture replacement ID bugfix (note: some textures from 1.5.4 may become incompatible) +* Adhoc multiplayer fixes (#8975) +* Vulkan support on Linux/SDL (#10413) * Retroarch support What's new in 1.5.4 From 5f9767d19e8280d9b11f2277638bb739fd5c6923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 29 Apr 2018 09:45:34 +0200 Subject: [PATCH 005/301] Remove misleading log --- Core/HLE/sceDisplay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 01521d4b34..2d9cef0500 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -714,7 +714,6 @@ void __DisplayFlip(int cyclesLate) { // Check first though, might've just quit / been paused. if (coreState == CORE_RUNNING) { coreState = CORE_NEXTFRAME; - DEBUG_LOG(SCEDISPLAY, "No recent flip - displaying anyway."); gpu->CopyDisplayToOutput(); if (fbReallyDirty) { actualFlips++; From 208b0aa4a7483c3ee50e4a6d21d877f9126d233a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 09:46:23 -0700 Subject: [PATCH 006/301] Savedata: Report secureVersion param usage. Seems likely this is what's happening in #10814, but need data from more games. --- Core/Dialog/SavedataParam.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index ff5a6d964a..1c4f27f99e 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -576,6 +576,9 @@ bool SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &save } bool SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode) { + if (param->secureVersion != 0) { + WARN_LOG_REPORT(SCEUTILITY, "Savedata version requested: %d", param->secureVersion); + } u8 *data_ = param->dataBuf; std::string filePath = dirPath+"/"+GetFileName(param); s64 readSize; From a85e914f78cfeff4d2defd16ee3aa9b80fbeec01 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 09:47:39 -0700 Subject: [PATCH 007/301] Vulkan: Fix use-after-free crash on shutdown. --- ext/native/thin3d/thin3d_vulkan.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 1344af1644..6f6a7dc735 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -788,7 +788,12 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) VKContext::~VKContext() { allocator_->Destroy(); - delete allocator_; + // We have to delete on queue, so this can free its queued deletions. + vulkan_->Delete().QueueCallback([](void *ptr) { + auto allocator = static_cast(ptr); + delete allocator; + }, allocator_); + allocator_ = nullptr; // This also destroys all descriptor sets. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].descSets_.clear(); From ae1bb20b7bcb0f42d4e5deab21d3fc3212f15111 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 10:23:03 -0700 Subject: [PATCH 008/301] GLES: Fix game screenshots. --- ext/native/thin3d/thin3d.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index b877eb6765..dfe833473c 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -372,6 +372,14 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u dst32 += dstStride; } } + } else if (format == Draw::DataFormat::R8G8B8_UNORM) { + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + memcpy(dst + x * 3, src32 + x, 3); + } + src32 += srcStride; + dst += dstStride * 3; + } } else { // But here it shouldn't matter if they do intersect uint16_t *dst16 = (uint16_t *)dst; From 170e871c54340028aab33c77b46d707ef796067f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 10:23:53 -0700 Subject: [PATCH 009/301] thin3d: Report unsupported format conversion. Better we know than ignore. --- ext/native/thin3d/thin3d.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index dfe833473c..c475af1513 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -6,6 +6,7 @@ #include "thin3d/thin3d.h" #include "Common/Log.h" #include "Common/ColorConv.h" +#include "Core/Reporting.h" namespace Draw { @@ -408,7 +409,7 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u case Draw::DataFormat::R8G8B8A8_UNORM: case Draw::DataFormat::UNDEFINED: default: - // Not possible. + WARN_LOG_REPORT_ONCE(convFromRGBA, G3D, "Unable to convert from format: %d", format); break; } } From 074f52b81933ee50530cc82ae624af22a38d88e9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 11:45:32 -0700 Subject: [PATCH 010/301] Android: Properly reset home on config reload. We reload the config once we know we have permission to read it, but that means we have to run any cleanup logic on it again. --- UI/NativeApp.cpp | 61 +++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 31105d96fb..2dd663f5f0 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -276,7 +276,7 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo } #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) -bool CheckFontIsUsable(const wchar_t *fontFace) { +static bool CheckFontIsUsable(const wchar_t *fontFace) { wchar_t actualFontFace[1024] = { 0 }; HFONT f = CreateFont(0, 0, 0, 0, FW_LIGHT, 0, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH, fontFace); @@ -298,6 +298,34 @@ bool CheckFontIsUsable(const wchar_t *fontFace) { } #endif +static void PostLoadConfig() { + // On Windows, we deal with currentDirectory in InitSysDirectories(). +#ifndef _WIN32 + if (g_Config.currentDirectory.empty()) { +#if defined(__ANDROID__) + g_Config.currentDirectory = g_Config.externalDirectory; +#elif defined(IOS) + g_Config.currentDirectory = g_Config.internalDataDirectory; +#else + if (getenv("HOME") != nullptr) + g_Config.currentDirectory = getenv("HOME"); + else + g_Config.currentDirectory = "./"; +#endif + } +#endif + + // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to + // test new languages without recompiling the entire app, which is a hassle). + const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/"; + + // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations. + if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath)) + i18nrepo.LoadIni(g_Config.sLanguageIni); + else + i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath); +} + void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir, bool fs) { net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times. @@ -336,8 +364,10 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } #endif -#if defined(__ANDROID__) g_Config.internalDataDirectory = savegame_dir; + g_Config.externalDirectory = external_dir; + +#if defined(__ANDROID__) // Maybe there should be an option to use internal memory instead, but I think // that for most people, using external memory (SDCard/USB Storage) makes the // most sense. @@ -375,7 +405,6 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Note that if we don't have storage permission here, loading the config will // fail and it will be set to the default. Later, we load again when we get permission. g_Config.Load(); - g_Config.externalDirectory = external_dir; #endif LogManager *logman = LogManager::GetInstance(); @@ -484,20 +513,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch if (fileToLog) LogManager::GetInstance()->ChangeFileLog(fileToLog); -#ifndef _WIN32 - if (g_Config.currentDirectory == "") { -#if defined(__ANDROID__) - g_Config.currentDirectory = external_dir; -#elif defined(IOS) || defined(_WIN32) - g_Config.currentDirectory = savegame_dir; -#else - if (getenv("HOME") != NULL) - g_Config.currentDirectory = getenv("HOME"); - else - g_Config.currentDirectory = "./"; -#endif - } -#endif + PostLoadConfig(); // Hard reset the logs. TODO: Get rid of this and read from config. #ifndef _WIN32 @@ -515,16 +531,6 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch logman->AddListener(logger); #endif - // Allow the lang directory to be overridden for testing purposes (e.g. Android, where it's hard to - // test new languages without recompiling the entire app, which is a hassle). - const std::string langOverridePath = g_Config.memStickDirectory + "PSP/SYSTEM/lang/"; - - // If we run into the unlikely case that "lang" is actually a file, just use the built-in translations. - if (!File::Exists(langOverridePath) || !File::IsDirectory(langOverridePath)) - i18nrepo.LoadIni(g_Config.sLanguageIni); - else - i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath); - if (System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS)) { if (System_GetPermissionStatus(SYSTEM_PERMISSION_STORAGE) != PERMISSION_STATUS_GRANTED) { System_AskForPermission(SYSTEM_PERMISSION_STORAGE); @@ -955,6 +961,7 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { // with a freshly generated one. ILOG("Reloading config after storage permission grant."); g_Config.Load(); + PostLoadConfig(); } } From 21468ce1e4ddc2cc28930a566ef7f2c3c50afc96 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 14:44:00 -0700 Subject: [PATCH 011/301] UI: Cleaning loading handling and use tags. The tags should let the tweens persist correctly. See #10974. --- UI/EmuScreen.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e9606fffb7..ffef7dd59f 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -897,9 +897,7 @@ void EmuScreen::CreateViews() { GameInfoBGView *loadingBG = root_->Add(new GameInfoBGView(gamePath_, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT))); TextView *loadingTextView = root_->Add(new TextView(sc->T(PSP_GetLoading()), new AnchorLayoutParams(bounds.centerX(), NONE, NONE, 40, true))); - - // Don't really need this, and it creates a lot of strings to translate... - loadingTextView->SetVisibility(V_INVISIBLE); + loadingTextView_ = loadingTextView; static const int symbols[4] = { I_CROSS, @@ -909,10 +907,17 @@ void EmuScreen::CreateViews() { }; Spinner *loadingSpinner = root_->Add(new Spinner(symbols, ARRAY_SIZE(symbols), new AnchorLayoutParams(NONE, NONE, 45, 45, true))); loadingSpinner_ = loadingSpinner; - loadingTextView->SetShadow(true); - loadingTextView_ = loadingTextView; - loadingViewColor_ = loadingTextView->AddTween(new CallbackColorTween(0x00FFFFFF, 0x00FFFFFF, 0.2f, &bezierEaseInOut)); + loadingBG->SetTag("LoadingBG"); + loadingTextView->SetTag("LoadingText"); + loadingSpinner->SetTag("LoadingSpinner"); + + // Don't really need this, and it creates a lot of strings to translate... + // Maybe just show "Loading game..." only? + loadingTextView->SetVisibility(V_GONE); + loadingTextView->SetShadow(true); + + loadingViewColor_ = loadingSpinner->AddTween(new CallbackColorTween(0x00FFFFFF, 0x00FFFFFF, 0.2f, &bezierEaseInOut)); loadingViewColor_->SetCallback([loadingBG, loadingTextView, loadingSpinner](View *v, uint32_t c) { loadingBG->SetColor(c & 0xFFC0C0C0); loadingTextView->SetTextColor(c); @@ -921,15 +926,18 @@ void EmuScreen::CreateViews() { loadingViewColor_->Persist(); // We start invisible here, in case of recreated views. - loadingViewVisible_ = loadingTextView->AddTween(new VisibilityTween(UI::V_INVISIBLE, UI::V_INVISIBLE, 0.2f, &bezierEaseInOut)); + loadingViewVisible_ = loadingSpinner->AddTween(new VisibilityTween(UI::V_INVISIBLE, UI::V_INVISIBLE, 0.2f, &bezierEaseInOut)); loadingViewVisible_->Persist(); - loadingViewVisible_->Finish.Add([loadingBG](EventParams &p) { + loadingViewVisible_->Finish.Add([loadingBG, loadingSpinner](EventParams &p) { loadingBG->SetVisibility(p.v->GetVisibility()); // If we just became invisible, flush BGs since we don't need them anymore. // Saves some VRAM for the game, but don't do it before we fade out... if (p.v->GetVisibility() == V_INVISIBLE) { g_gameInfoCache->FlushBGs(); + // And we can go away too. This means the tween will never run again. + loadingBG->SetVisibility(V_GONE); + loadingSpinner->SetVisibility(V_GONE); } return EVENT_DONE; }); @@ -1203,7 +1211,7 @@ void EmuScreen::render() { if (invalid_) return; - const bool hasVisibleUI = !osm.IsEmpty() || saveStatePreview_->GetVisibility() != UI::V_GONE || g_Config.bShowTouchControls || loadingTextView_->GetVisibility() == UI::V_VISIBLE; + const bool hasVisibleUI = !osm.IsEmpty() || saveStatePreview_->GetVisibility() != UI::V_GONE || g_Config.bShowTouchControls || loadingSpinner_->GetVisibility() == UI::V_VISIBLE; const bool showDebugUI = g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler; if (hasVisibleUI || showDebugUI || g_Config.iShowFPSCounter != 0) { renderUI(); From b6472b562b7f52381744790b935af212f0ada80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 2 May 2018 10:07:37 +0200 Subject: [PATCH 012/301] Revert "Limit the flip delay in the other direction to try to work around #10763." --- Core/CoreTiming.cpp | 1 - Core/HLE/sceDisplay.cpp | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 5cebf2f3a5..78ca1d2fb8 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -585,7 +585,6 @@ void Advance() int cyclesExecuted = slicelength - currentMIPS->downcount; globalTimer += cyclesExecuted; currentMIPS->downcount = slicelength; - VERBOSE_LOG(SCEDISPLAY, "CoreTiming: Event type '%s'", event_types[first->type].name); if (Common::AtomicLoadAcquire(hasTsEvents)) MoveEvents(); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 2d9cef0500..8c7fd13597 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -588,7 +588,6 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { static void DoFrameIdleTiming() { PROFILE_THIS_SCOPE("timing"); - if (!FrameTimingThrottled() || !g_Config.bEnableSound || wasPaused) { return; } @@ -911,8 +910,6 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) // sceDisplaySetFramebuf() isn't supposed to delay threads at all. This is a hack. // So let's only delay when it's more than 1ms. const s64 FLIP_DELAY_CYCLES_MIN = usToCycles(1000); - // Though if we are super early, we also don't want to delay because the game is being silly like in #10763 - const s64 FLIP_DELAY_CYCLES_MAX = usToCycles(16000); // This is slightly less than a full frame at 60hz, 16666 // Some games (like Final Fantasy 4) only call this too much in spurts. // The goal is to fix games where this would result in a consistent overhead. const int FLIP_DELAY_MIN_FLIPS = 30; @@ -922,7 +919,7 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) u64 now = CoreTiming::GetTicks(); s64 cyclesAhead = nextFlipCycles - now; - if (cyclesAhead > FLIP_DELAY_CYCLES_MIN && (cyclesAhead < FLIP_DELAY_CYCLES_MAX || PSP_CoreParameter().unthrottle)) { + if (cyclesAhead > FLIP_DELAY_CYCLES_MIN) { if (lastFlipsTooFrequent >= FLIP_DELAY_MIN_FLIPS && gpuStats.numClears > 0) { delayCycles = cyclesAhead; } else { From 844015b7f8d627b50e0c28cd3254f0938fa4fc36 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 2 May 2018 19:23:49 -0700 Subject: [PATCH 013/301] Vulkan: Discard draws immediately before clear. Fixes #10163, a crash on Adreno when a clear is the last thing in a render pass. --- ext/native/thin3d/VulkanRenderManager.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 373b911161..9e965ed3f4 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -616,14 +616,19 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); if (!clearMask) return; - // If this is the first drawing command, merge it into the pass. - if (curRenderStep_->render.numDraws == 0) { + // If this is the first drawing command or clears everything, merge it into the pass. + int allAspects = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + if (curRenderStep_->render.numDraws == 0 || clearMask == allAspects) { curRenderStep_->render.clearColor = clearColor; curRenderStep_->render.clearDepth = clearZ; curRenderStep_->render.clearStencil = clearStencil; curRenderStep_->render.color = (clearMask & VK_IMAGE_ASPECT_COLOR_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; curRenderStep_->render.depth = (clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; curRenderStep_->render.stencil = (clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) ? VKRRenderPassAction::CLEAR : VKRRenderPassAction::KEEP; + + // In case there were commands already. + curRenderStep_->render.numDraws = 0; + curRenderStep_->commands.clear(); } else { VkRenderData data{ VKRRenderCommand::CLEAR }; data.clear.clearColor = clearColor; From 954f93046fec2caf8adc3beb90e4c100b5403b67 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 3 May 2018 07:05:36 -0700 Subject: [PATCH 014/301] Vulkan: Keep state on Clear cmds when used later. This also removes duplicate state - for example our UI has some duplicate Scissor commands. --- ext/native/thin3d/VulkanQueueRunner.cpp | 7 +++ ext/native/thin3d/VulkanQueueRunner.h | 1 + ext/native/thin3d/VulkanRenderManager.cpp | 75 ++++++++++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 2737ff59ee..8ecbdbba86 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -611,6 +611,10 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { ILOG("RenderPass Begin(%x)", fb); for (auto &cmd : pass.commands) { switch (cmd.cmd) { + case VKRRenderCommand::REMOVED: + ILOG(" REMOVED"); + break; + case VKRRenderCommand::BIND_PIPELINE: ILOG(" BindPipeline(%x)", (int)(intptr_t)cmd.pipeline.pipeline); break; @@ -733,6 +737,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c for (const auto &c : commands) { switch (c.cmd) { + case VKRRenderCommand::REMOVED: + break; + case VKRRenderCommand::BIND_PIPELINE: if (c.pipeline.pipeline != lastPipeline) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline); diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index d743bda730..8d5b229139 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -16,6 +16,7 @@ enum { }; enum class VKRRenderCommand : uint8_t { + REMOVED, BIND_PIPELINE, STENCIL, BLEND, diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 9e965ed3f4..a68467b04a 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -1,3 +1,4 @@ +#include #include #include "Common/Log.h" @@ -612,6 +613,70 @@ bool VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { return true; } +static void RemoveDrawCommands(std::vector *cmds) { + // Here we remove any DRAW type commands when we hit a CLEAR. + cmds->erase(std::remove_if(cmds->begin(), cmds->end(), [](const VkRenderData &data) { + return data.cmd == VKRRenderCommand::DRAW || data.cmd == VKRRenderCommand::DRAW_INDEXED; + }), cmds->end()); +} + +static void CleanupRenderCommands(std::vector *cmds) { + size_t lastCommand[256]; + memset(lastCommand, -1, sizeof(lastCommand)); + + // Find any duplicate state commands (likely from RemoveDrawCommands.) + for (size_t i = 0; i < cmds->size(); ++i) { + auto &c = cmds->at(i); + auto &lastOfCmd = lastCommand[(uint8_t)c.cmd]; + + switch (c.cmd) { + case VKRRenderCommand::REMOVED: + // Ignore, in case some other code starts using this. + continue; + + case VKRRenderCommand::BIND_PIPELINE: + case VKRRenderCommand::VIEWPORT: + case VKRRenderCommand::SCISSOR: + case VKRRenderCommand::BLEND: + case VKRRenderCommand::STENCIL: + if (lastOfCmd != -1) { + cmds->at(lastOfCmd).cmd = VKRRenderCommand::REMOVED; + } + break; + + case VKRRenderCommand::PUSH_CONSTANTS: + // TODO: For now, we have to keep this one (it has an offset.) Still update lastCommand. + break; + + case VKRRenderCommand::CLEAR: + // Ignore, doesn't participate in state. + continue; + + case VKRRenderCommand::DRAW_INDEXED: + case VKRRenderCommand::DRAW: + default: + // Boundary - must keep state before this. + memset(lastCommand, -1, sizeof(lastCommand)); + continue; + } + + lastOfCmd = i; + } + + // At this point, anything in lastCommand can be cleaned up too. + // Note that it's safe to remove the last unused PUSH_CONSTANTS here. + for (size_t i = 0; i < ARRAY_SIZE(lastCommand); ++i) { + auto &lastOfCmd = lastCommand[i]; + if (lastOfCmd != -1) { + cmds->at(lastOfCmd).cmd = VKRRenderCommand::REMOVED; + } + } + + cmds->erase(std::remove_if(cmds->begin(), cmds->end(), [](const VkRenderData &data) { + return data.cmd == VKRRenderCommand::REMOVED; + }), cmds->end()); +} + void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); if (!clearMask) @@ -628,7 +693,7 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten // In case there were commands already. curRenderStep_->render.numDraws = 0; - curRenderStep_->commands.clear(); + RemoveDrawCommands(&curRenderStep_->commands); } else { VkRenderData data{ VKRRenderCommand::CLEAR }; data.clear.clearColor = clearColor; @@ -738,6 +803,14 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in void VulkanRenderManager::Finish() { curRenderStep_ = nullptr; + + // Let's do just a bit of cleanup on render commands now. + for (auto &step : steps_) { + if (step->stepType == VKRStepType::RENDER) { + CleanupRenderCommands(&step->commands); + } + } + int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; if (!useThread_) { From ec5d1a8722e57ae1a13640fadced1b81f8f5c254 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 3 May 2018 08:04:21 -0700 Subject: [PATCH 015/301] Vulkan: Leave removed commands in render passes. We'll just need to not check commands.empty(), which we do in a couple places but not in important ways so far. --- ext/native/thin3d/VulkanQueueRunner.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 8ecbdbba86..6623112428 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -612,7 +612,7 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { for (auto &cmd : pass.commands) { switch (cmd.cmd) { case VKRRenderCommand::REMOVED: - ILOG(" REMOVED"); + ILOG(" (Removed)"); break; case VKRRenderCommand::BIND_PIPELINE: diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a68467b04a..e4a6796934 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -615,9 +615,11 @@ bool VulkanRenderManager::InitDepthStencilBuffer(VkCommandBuffer cmd) { static void RemoveDrawCommands(std::vector *cmds) { // Here we remove any DRAW type commands when we hit a CLEAR. - cmds->erase(std::remove_if(cmds->begin(), cmds->end(), [](const VkRenderData &data) { - return data.cmd == VKRRenderCommand::DRAW || data.cmd == VKRRenderCommand::DRAW_INDEXED; - }), cmds->end()); + for (auto &c : *cmds) { + if (c.cmd == VKRRenderCommand::DRAW || c.cmd == VKRRenderCommand::DRAW_INDEXED) { + c.cmd = VKRRenderCommand::REMOVED; + } + } } static void CleanupRenderCommands(std::vector *cmds) { @@ -631,7 +633,6 @@ static void CleanupRenderCommands(std::vector *cmds) { switch (c.cmd) { case VKRRenderCommand::REMOVED: - // Ignore, in case some other code starts using this. continue; case VKRRenderCommand::BIND_PIPELINE: @@ -671,10 +672,6 @@ static void CleanupRenderCommands(std::vector *cmds) { cmds->at(lastOfCmd).cmd = VKRRenderCommand::REMOVED; } } - - cmds->erase(std::remove_if(cmds->begin(), cmds->end(), [](const VkRenderData &data) { - return data.cmd == VKRRenderCommand::REMOVED; - }), cmds->end()); } void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { From 3f7602227cee8c953a5e45fa0df2f1dbaf6539ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 May 2018 22:18:26 +0200 Subject: [PATCH 016/301] Windows: If neither OpenGL nor D3D9 is available, offer an escape route into D3D11. This does happen on some fresh Win10 installations. We really should do something much nicer and more automatic, but that's for later. --- Windows/GPU/WindowsGLContext.cpp | 7 +++++-- ext/native/thin3d/VulkanQueueRunner.h | 1 + ext/native/thin3d/VulkanRenderManager.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 43eeaca913..7004fdde24 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -249,7 +249,7 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { ExitProcess(1); } const char *defaultError = "Insufficient OpenGL driver support detected!\n\n" - "Your GPU reports that it does not support OpenGL 2.0. Would you like to try using DirectX 9 instead?\n\n" + "Your GPU reports that it does not support OpenGL 2.0. Would you like to try using DirectX instead?\n\n" "DirectX is currently compatible with less games, but on your GPU it may be the only choice.\n\n" "Visit the forums at http://forums.ppsspp.org for more information.\n\n"; @@ -262,7 +262,10 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { if (yes) { // Change the config to D3D and restart. - g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D9; + const char *d3d9Or11 = "Direct3D 9? (Or no for Direct3D 11)"; + std::wstring whichD3D9 = ConvertUTF8ToWString(err->T("D3D9or11", d3d9Or11)); + bool d3d9 = IDYES == MessageBox(hWnd_, whichD3D9.c_str(), title.c_str(), MB_YESNO); + g_Config.iGPUBackend = d3d9 ? (int)GPUBackend::DIRECT3D9 : (int)GPUBackend::DIRECT3D11; g_Config.Save(); W32Util::ExitAndRestart(); diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 8d5b229139..3ff8e161ea 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -26,6 +26,7 @@ enum class VKRRenderCommand : uint8_t { DRAW, DRAW_INDEXED, PUSH_CONSTANTS, + NUM_RENDER_COMMANDS, }; struct VkRenderData { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index e4a6796934..a7cec21ed8 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -623,7 +623,7 @@ static void RemoveDrawCommands(std::vector *cmds) { } static void CleanupRenderCommands(std::vector *cmds) { - size_t lastCommand[256]; + size_t lastCommand[(int)VKRRenderCommand::NUM_RENDER_COMMANDS]; memset(lastCommand, -1, sizeof(lastCommand)); // Find any duplicate state commands (likely from RemoveDrawCommands.) From a3bbe8791454697c4d170e1387cbb73f8fdacc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 May 2018 23:11:39 +0200 Subject: [PATCH 017/301] D3D11 CopyFBToMemory: Clamp rect to fb size. Last-minute style fix, should really debug where the bad parameters came from. Affects MGS Peace Walker intro --- ext/native/thin3d/thin3d_d3d11.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 9cb46b3fc6..2ff8388ca0 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1365,6 +1365,16 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); } + // TODO: Figure out where the badness really comes from. + if (bx + bw > fb->width) { + bw -= (bx + bw) - fb->width; + } + if (by + bh > fb->height) { + bh -= (by + bh) - fb->height; + } + if (bh <= 0 || bw <= 0) + return true; + bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT; ID3D11Texture2D *packTex; From 17259b87ea6068b9035f171e456777656f76f92b Mon Sep 17 00:00:00 2001 From: aliaspider Date: Fri, 4 May 2018 17:37:46 +0100 Subject: [PATCH 018/301] softgpu: remove RIP addressing from SamplerX86 --- GPU/Software/SamplerX86.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index faee56e718..a0c0a3ea91 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -211,11 +211,18 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) { CVTDQ2PS(fpScratchReg5, R(fpScratchReg5)); SHUFPS(fpScratchReg5, R(fpScratchReg5), _MM_SHUFFLE(0, 0, 0, 0)); if (RipAccessible(by256)) { - MULPS(fpScratchReg5, M(by256)); // rip accessible + MULPS(fpScratchReg5, M(by256)); } else { - Crash(); // TODO + MOV(PTRBITS, R(tempReg1), ImmPtr(by256)); + MULPS(fpScratchReg5, MatR(tempReg1)); + } + + if (RipAccessible(ones)) { + MOVAPS(XMM0, M(ones)); + } else { + MOV(PTRBITS, R(tempReg1), ImmPtr(ones)); + MOVAPS(XMM0, MatR(tempReg1)); } - MOVAPS(XMM0, M(ones)); SUBPS(XMM0, R(fpScratchReg5)); MULPS(fpScratchReg1, R(XMM0)); @@ -231,8 +238,18 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) { MOVD_xmm(fpScratchReg5, MDisp(RSP, 32)); CVTDQ2PS(fpScratchReg5, R(fpScratchReg5)); SHUFPS(fpScratchReg5, R(fpScratchReg5), _MM_SHUFFLE(0, 0, 0, 0)); - MULPS(fpScratchReg5, M(by256)); - MOVAPS(XMM0, M(ones)); + if (RipAccessible(ones)) { + MULPS(fpScratchReg5, M(by256)); + } else { + MOV(PTRBITS, R(tempReg1), ImmPtr(by256)); + MULPS(fpScratchReg5, MatR(tempReg1)); + } + if (RipAccessible(ones)) { + MOVAPS(XMM0, M(ones)); + } else { + MOV(PTRBITS, R(tempReg1), ImmPtr(ones)); + MOVAPS(XMM0, MatR(tempReg1)); + } SUBPS(XMM0, R(fpScratchReg5)); MULPS(fpScratchReg1, R(XMM0)); @@ -556,9 +573,10 @@ bool SamplerJitCache::Jit_Decode4444() { MOVD_xmm(fpScratchReg1, R(resultReg)); PUNPCKLBW(fpScratchReg1, R(fpScratchReg1)); if (RipAccessible(color4444mask)) { - PAND(fpScratchReg1, M(color4444mask)); // rip accessible + PAND(fpScratchReg1, M(color4444mask)); } else { - Crash(); + MOV(PTRBITS, R(tempReg1), ImmPtr(color4444mask)); + PAND(fpScratchReg1, MatR(tempReg1)); } MOVSS(fpScratchReg2, R(fpScratchReg1)); MOVSS(fpScratchReg3, R(fpScratchReg1)); From 698bd75209b9f12d9e3d4f1df09471bd5591eade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 4 May 2018 22:20:41 +0200 Subject: [PATCH 019/301] Fix regression caused by the new depal code for Vulkan. Fixes #10993 --- GPU/GLES/TextureCacheGLES.cpp | 4 ++-- GPU/GPUState.h | 6 ++++++ GPU/Vulkan/DrawEngineVulkan.h | 5 ++++- GPU/Vulkan/TextureCacheVulkan.cpp | 12 ++++++++---- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 096b634c8d..40ef4f0338 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -328,7 +328,7 @@ void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { lastBoundTexture = entry->textureName; } UpdateSamplingParams(*entry, false); - gstate_c.useShaderDepal = false; + gstate_c.SetUseShaderDepal(false); } void TextureCacheGLES::Unbind() { @@ -461,7 +461,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram // Since we started/ended render passes, might need these. gstate_c.Dirty(DIRTY_DEPAL); - gstate_c.useShaderDepal = true; + gstate_c.SetUseShaderDepal(true); gstate_c.depalFramebufferFormat = framebuffer->drawnFormat; const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index e6724098c3..06b14867c4 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -517,6 +517,12 @@ struct GPUStateCache { bool IsDirty(u64 what) const { return (dirty & what) != 0ULL; } + void SetUseShaderDepal(bool depal) { + if (depal != useShaderDepal) { + useShaderDepal = depal; + Dirty(DIRTY_FRAGMENTSHADER_STATE); + } + } void SetTextureFullAlpha(bool fullAlpha) { if (fullAlpha != textureFullAlpha) { textureFullAlpha = fullAlpha; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 0aec77065e..529b3b8071 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -180,7 +180,10 @@ public: void SetLineWidth(float lineWidth); void SetDepalTexture(VkImageView depal) { - boundDepal_ = depal; + if (boundDepal_ != depal) { + boundDepal_ = depal; + gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); + } } private: diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 4d1a8bfe66..6dbb6a7255 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -328,7 +328,7 @@ void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) { UpdateSamplingParams(*entry, key); curSampler_ = samplerCache_.GetOrCreateSampler(key); drawEngine_->SetDepalTexture(VK_NULL_HANDLE); - gstate_c.useShaderDepal = false; + gstate_c.SetUseShaderDepal(false); } void TextureCacheVulkan::Unbind() { @@ -344,7 +344,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr DepalShaderVulkan *depalShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; - bool useShaderDepal = true; + bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer; if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { if (useShaderDepal) { @@ -356,9 +356,9 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr samplerKey.magFilt = false; samplerKey.minFilt = false; samplerKey.mipFilt = false; - // Make sure to update the uniforms. + // Make sure to update the uniforms, and also texture - needs a recheck. gstate_c.Dirty(DIRTY_DEPAL); - gstate_c.useShaderDepal = true; + gstate_c.SetUseShaderDepal(true); gstate_c.depalFramebufferFormat = framebuffer->drawnFormat; const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; @@ -370,6 +370,8 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr return; } else { depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); + drawEngine_->SetDepalTexture(nullptr); + gstate_c.SetUseShaderDepal(false); } } if (depalShader) { @@ -466,6 +468,8 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr framebufferManager_->RebindFramebuffer(); // TODO: This line should usually not be needed. imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); + drawEngine_->SetDepalTexture(nullptr); + gstate_c.SetUseShaderDepal(false); gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); } From 4adb1263af60c5db5ec2ae8fabfc41e58bbbe340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 4 May 2018 22:36:19 +0200 Subject: [PATCH 020/301] Update lang submodule --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 44a654736d..8ea7353697 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 44a654736d67b36a55171b1862d41df2dc19cb25 +Subproject commit 8ea7353697cdd3d308676966644b8ef6ba82786e From 77e574ccc672b9ad9376bb75198159fd7146b221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 4 May 2018 22:57:13 +0200 Subject: [PATCH 021/301] Buildfix. --- GPU/Vulkan/TextureCacheVulkan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 6dbb6a7255..38831d1e4b 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -370,7 +370,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr return; } else { depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); - drawEngine_->SetDepalTexture(nullptr); + drawEngine_->SetDepalTexture(VK_NULL_HANDLE); gstate_c.SetUseShaderDepal(false); } } @@ -468,7 +468,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr framebufferManager_->RebindFramebuffer(); // TODO: This line should usually not be needed. imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); - drawEngine_->SetDepalTexture(nullptr); + drawEngine_->SetDepalTexture(VK_NULL_HANDLE); gstate_c.SetUseShaderDepal(false); gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); From 94ab646cd4c8c81a06bba58290736a85292eca18 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 5 May 2018 19:16:51 -0700 Subject: [PATCH 022/301] Correct compatibility server link. Now it's https. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ec7d514b14..b85e74f8a4 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For the latest source code, see [our github page](https://github.com/hrydgard/pp For build instructions and other development tutorials, see [the wiki](https://github.com/hrydgard/ppsspp/wiki). -For game compatibility, see [community compatibility feedback](http://report.ppsspp.org/games). +For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games). What's new in 1.5.4 From 8d07e6d985b24c84dabb862e5d92e5d420f4a315 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 May 2018 08:57:44 -0700 Subject: [PATCH 023/301] GPU: Prevent temp FBOs from overwriting each other. Sometimes we'd use two temp FBOs in the same draw (e.g. shader blending + depal.) This could cause the same temp FBO to get used for two purposes, causing weird behavior. --- GPU/Common/FramebufferCommon.cpp | 8 ++++---- GPU/Common/FramebufferCommon.h | 13 ++++++++++--- GPU/D3D11/FramebufferManagerD3D11.cpp | 4 ++-- GPU/D3D11/TextureCacheD3D11.cpp | 2 +- GPU/Directx9/FramebufferDX9.cpp | 15 ++------------- GPU/Directx9/FramebufferDX9.h | 5 ----- GPU/Directx9/TextureCacheDX9.cpp | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 2 +- GPU/GLES/StencilBufferGLES.cpp | 2 +- GPU/GLES/TextureCacheGLES.cpp | 2 +- GPU/Vulkan/FramebufferVulkan.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 3 +-- 12 files changed, 25 insertions(+), 35 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 8e8a023351..2d384e2ee4 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1832,8 +1832,8 @@ void FramebufferManagerCommon::GetCardboardSettings(CardboardSettings *cardboard cardboardSettings->screenHeight = cardboardScreenHeight; } -Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(u16 w, u16 h, Draw::FBColorDepth depth) { - u64 key = ((u64)depth << 32) | ((u32)w << 16) | h; +Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u16 h, Draw::FBColorDepth depth) { + u64 key = ((u64)reason << 48) | ((u64)depth << 32) | ((u32)w << 16) | h; auto it = tempFBOs_.find(key); if (it != tempFBOs_.end()) { it->second.last_frame_used = gpuStats.numFlips; @@ -1845,7 +1845,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(u16 w, u16 h, Draw::FBCo if (!fbo) return fbo; - const TempFBO info = { fbo, gpuStats.numFlips }; + const TempFBOInfo info = { fbo, gpuStats.numFlips }; tempFBOs_[key] = info; return fbo; } @@ -1914,7 +1914,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB w = vfb->width * maxRes; h = vfb->height * maxRes; - Draw::Framebuffer *tempFBO = GetTempFBO(w, h); + Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::COPY, w, h); VirtualFramebuffer tempVfb = *vfb; tempVfb.fbo = tempFBO; tempVfb.bufferWidth = vfb->width; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index f96174c0c3..be1e0ae115 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -153,6 +153,13 @@ enum DrawTextureFlags { DRAWTEX_KEEP_STENCIL_ALPHA = 4, }; +enum class TempFBO { + DEPAL, + BLIT, + // For copies of framebuffers (e.g. shader blending.) + COPY, +}; + inline Draw::DataFormat GEFormatToThin3D(int geFormat) { switch (geFormat) { case GE_FORMAT_4444: @@ -290,7 +297,7 @@ public: virtual void Resized(); - Draw::Framebuffer *GetTempFBO(u16 w, u16 h, Draw::FBColorDepth colorDepth = Draw::FBO_8888); + Draw::Framebuffer *GetTempFBO(TempFBO reason, u16 w, u16 h, Draw::FBColorDepth colorDepth = Draw::FBO_8888); // Debug features virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes); @@ -400,12 +407,12 @@ protected: bool needGLESRebinds_ = false; - struct TempFBO { + struct TempFBOInfo { Draw::Framebuffer *fbo; int last_frame_used; }; - std::map tempFBOs_; + std::map tempFBOs_; std::vector fbosToDelete_; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 9cd78f97dc..17c9cec5e1 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -544,7 +544,7 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr // Currently rendering to this framebuffer. Need to make a copy. if (!skipCopy && framebuffer == currentRenderVfb_) { // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. - Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); + Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); if (renderCopy) { VirtualFramebuffer copyInfo = *framebuffer; copyInfo.fbo = renderCopy; @@ -671,7 +671,7 @@ void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, // Direct3D doesn't support rect -> self. Draw::Framebuffer *srcFBO = src->fbo; if (src == dst) { - Draw::Framebuffer *tempFBO = GetTempFBO(src->renderWidth, src->renderHeight, (Draw::FBColorDepth)src->colorDepth); + Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::BLIT, src->renderWidth, src->renderHeight, (Draw::FBColorDepth)src->colorDepth); SimpleBlit(tempFBO, dstX1, dstY1, dstX2, dstY2, src->fbo, srcX1, srcY1, srcX2, srcY2, false); srcFBO = tempFBO; } diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 31c37bd207..4c9cc96575 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -387,7 +387,7 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFra const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); ID3D11ShaderResourceView *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_, expand32); - Draw::Framebuffer *depalFBO = framebufferManagerD3D11_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + Draw::Framebuffer *depalFBO = framebufferManagerD3D11_->GetTempFBO(TempFBO::DEPAL, framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); shaderManager_->DirtyLastShader(); draw_->BindPipeline(nullptr); diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 3d7e7c03b7..d2f189ad76 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -119,9 +119,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { if (drawPixelsTex_) { drawPixelsTex_->Release(); } - for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { - it->second.fbo->Release(); - } for (auto it = offscreenSurfaces_.begin(), end = offscreenSurfaces_.end(); it != end; ++it) { it->second.surface->Release(); } @@ -448,7 +445,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } if (!skipCopy && currentRenderVfb_ && framebuffer->fb_address == gstate.getFrameBufRawAddress()) { // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. - Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); + Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); if (renderCopy) { VirtualFramebuffer copyInfo = *framebuffer; copyInfo.fbo = renderCopy; @@ -514,7 +511,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { // Direct3D 9 doesn't support rect -> self. Draw::Framebuffer *srcFBO = src->fbo; if (src == dst) { - Draw::Framebuffer *tempFBO = GetTempFBO(src->renderWidth, src->renderHeight, (Draw::FBColorDepth)src->colorDepth); + Draw::Framebuffer *tempFBO = GetTempFBO(TempFBO::BLIT, src->renderWidth, src->renderHeight, (Draw::FBColorDepth)src->colorDepth); bool result = draw_->BlitFramebuffer( src->fbo, srcX1, srcY1, srcX2, srcY2, tempFBO, dstX1, dstY1, dstX2, dstY2, @@ -709,11 +706,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } bvfbs_.clear(); - for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { - it->second.fbo->Release(); - } - tempFBOs_.clear(); - for (auto it = offscreenSurfaces_.begin(), end = offscreenSurfaces_.end(); it != end; ++it) { it->second.surface->Release(); } @@ -761,9 +753,6 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { if (offscreen) { success = GetRenderTargetFramebuffer(renderTarget, offscreen, w, h, buffer); } - if (tempFBO) { - tempFBO->Release(); - } } return success; diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index bc2ce7c31d..004f7bb944 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -110,16 +110,11 @@ private: ShaderManagerDX9 *shaderManagerDX9_; DrawEngineDX9 *drawEngineD3D9_; - struct TempFBO { - Draw::Framebuffer *fbo; - int last_frame_used; - }; struct OffscreenSurface { LPDIRECT3DSURFACE9 surface; int last_frame_used; }; - std::map tempFBOs_; std::map offscreenSurfaces_; #if 0 diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 85a13ab820..6d4338117e 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -426,7 +426,7 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); LPDIRECT3DTEXTURE9 clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - Draw::Framebuffer *depalFBO = framebufferManagerDX9_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + Draw::Framebuffer *depalFBO = framebufferManagerDX9_->GetTempFBO(TempFBO::DEPAL, framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); shaderManager_->DirtyLastShader(); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 1211459f02..851c8b698a 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -544,7 +544,7 @@ void FramebufferManagerGLES::BindFramebufferAsColorTexture(int stage, VirtualFra } if (!skipCopy && currentRenderVfb_ && framebuffer->fb_address == gstate.getFrameBufRawAddress()) { // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. - Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); + Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); if (renderCopy) { VirtualFramebuffer copyInfo = *framebuffer; copyInfo.fbo = renderCopy; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 4b59b60b96..fcbba39970 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -154,7 +154,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe Draw::Framebuffer *blitFBO = nullptr; if (useBlit) { - blitFBO = GetTempFBO(w, h, Draw::FBO_8888); + blitFBO = GetTempFBO(TempFBO::COPY, w, h, Draw::FBO_8888); draw_->BindFramebufferAsRenderTarget(blitFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); } else if (dstBuffer->fbo) { draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 40ef4f0338..8f6368c66b 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -475,7 +475,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram if (depal) { const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); GLRTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - Draw::Framebuffer *depalFBO = framebufferManagerGL_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + Draw::Framebuffer *depalFBO = framebufferManagerGL_->GetTempFBO(TempFBO::DEPAL, framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); shaderManager_->DirtyLastShader(); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 73495acca5..e731802691 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -435,7 +435,7 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V // Currently rendering to this framebuffer. Need to make a copy. if (!skipCopy && framebuffer == currentRenderVfb_) { // TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size. - Draw::Framebuffer *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); + Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight, (Draw::FBColorDepth)framebuffer->colorDepth); if (renderCopy) { VirtualFramebuffer copyInfo = *framebuffer; copyInfo.fbo = renderCopy; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 38831d1e4b..8553985564 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -379,8 +379,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO( - framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); + Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO(TempFBO::DEPAL, framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); Vulkan2D::Vertex verts[4] = { From abbab23814b06dbf0f9a21cb2b43bcce6e9faaa7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 May 2018 08:58:54 -0700 Subject: [PATCH 024/301] D3D11: Use a dedicated slot for the depal texture. Otherwise it affects shader blending. --- GPU/Common/DepalettizeShaderCommon.cpp | 2 +- GPU/D3D11/TextureCacheD3D11.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 0ff61cc995..c65db54599 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -36,7 +36,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang if (language == HLSL_D3D11) { WRITE(p, "SamplerState texSamp : register(s0);\n"); WRITE(p, "Texture2D tex : register(t0);\n"); - WRITE(p, "Texture2D pal : register(t1);\n"); + WRITE(p, "Texture2D pal : register(t3);\n"); } else if (language == GLSL_VULKAN) { WRITE(p, "#version 140\n"); WRITE(p, "#extension GL_ARB_separate_shader_objects : enable\n"); diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 4c9cc96575..3759e9c758 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -398,7 +398,8 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFra shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset, xoff, yoff); shaderApply.Use(depalShaderCache_->GetDepalettizeVertexShader(), depalShaderCache_->GetInputLayout()); - context_->PSSetShaderResources(1, 1, &clutTexture); + context_->PSSetShaderResources(3, 1, &clutTexture); + context_->PSSetSamplers(3, 1, &stockD3D11.samplerPoint2DWrap); framebufferManagerD3D11_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY); context_->PSSetSamplers(0, 1, &stockD3D11.samplerPoint2DWrap); draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); From f4e8e68c442e44afac2ce82f0d858be50d1b8960 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 May 2018 10:15:05 -0700 Subject: [PATCH 025/301] Io: Cleanup file not found error codes. Replay (#10888) caused error to be non zero, which was later translated to something else, confusingly... --- Core/FileSystems/DirectoryFileSystem.cpp | 10 ++++++++-- Core/HLE/sceIo.cpp | 18 ++++++++---------- Core/HLE/sceKernel.h | 1 + Core/HLE/sceKernelModule.cpp | 3 +-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index dc1f6a1ac2..00177fae73 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -181,8 +181,10 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil #if HOST_IS_CASE_SENSITIVE if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE)) { DEBUG_LOG(FILESYS, "Checking case for path %s", fileName.c_str()); - if (!FixPathCase(basePath, fileName, FPC_PATH_MUST_EXIST) ) + if (!FixPathCase(basePath, fileName, FPC_PATH_MUST_EXIST)) { + error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; return false; // or go on and attempt (for a better error code than just 0?) + } } // else we try fopen first (in case we're lucky) before simulating case insensitivity #endif @@ -250,6 +252,8 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil I18NCategory *err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; + } else { + error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; } } #else @@ -310,6 +314,8 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil I18NCategory *err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; + } else { + error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; } #endif @@ -597,7 +603,7 @@ u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const u32 err = 0; bool success = entry.hFile.Open(basePath, filename, access, err); if (err == 0 && !success) { - err = -1; + err = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; } err = ReplayApplyDisk(ReplayAction::FILE_OPEN, err, CoreTiming::GetGlobalTimeUs()); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 8935e0dc3c..f2f5eebf6b 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -62,7 +62,6 @@ extern "C" { // For headless screenshots. #include "Core/HLE/sceDisplay.h" -static const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; static const int ERROR_ERRNO_IO_ERROR = 0x80010005; static const int ERROR_MEMSTICK_DEVCTL_BAD_PARAMS = 0x80220081; static const int ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS = 0x80220082; @@ -805,7 +804,7 @@ static u32 sceIoGetstat(const char *filename, u32 addr) { } } else { DEBUG_LOG(SCEIO, "sceIoGetstat(%s, %08x) : FILE NOT FOUND", filename, addr); - return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "io getstat", usec); + return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "io getstat", usec); } } @@ -1383,7 +1382,7 @@ static u32 sceIoOpen(const char *filename, int flags, int mode) { else { ERROR_LOG(SCEIO, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", filename, flags, mode); - return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file opened", 10000); + return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "file opened", 10000); } } @@ -1412,7 +1411,7 @@ static u32 sceIoRemove(const char *filename) { // TODO: This timing isn't necessarily accurate, low end for now. if(!pspFileSystem.GetFileInfo(filename).exists) - return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file removed", 100); + return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "file removed", 100); pspFileSystem.RemoveFile(filename); return hleDelayResult(0, "file removed", 100); @@ -1433,7 +1432,7 @@ static u32 sceIoRmdir(const char *dirname) { if (pspFileSystem.RmDir(dirname)) return hleDelayResult(0, "rmdir", 1000); else - return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "rmdir", 1000); + return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "rmdir", 1000); } static u32 sceIoSync(const char *devicename, int flag) { @@ -1855,7 +1854,7 @@ static u32 sceIoRename(const char *from, const char *to) { // TODO: Timing isn't terribly accurate. if (!pspFileSystem.GetFileInfo(from).exists) - return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file renamed", 1000); + return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "file renamed", 1000); int result = pspFileSystem.RenameFile(from, to); if (result < 0) @@ -1933,7 +1932,7 @@ static u32 sceIoOpenAsync(const char *filename, int flags, int mode) f = new FileNode(); f->handle = kernelObjects.Create(f); f->fullpath = filename; - f->asyncResult = error == 0 ? ERROR_ERRNO_FILE_NOT_FOUND : error; + f->asyncResult = error == 0 ? SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND : error; f->closePending = true; fd = __IoAllocFd(f); @@ -2142,9 +2141,8 @@ public: static u32 sceIoDopen(const char *path) { DEBUG_LOG(SCEIO, "sceIoDopen(\"%s\")", path); - if(!pspFileSystem.GetFileInfo(path).exists) - { - return ERROR_ERRNO_FILE_NOT_FOUND; + if (!pspFileSystem.GetFileInfo(path).exists) { + return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; } DirListing *dir = new DirListing(); diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 7bc309717f..fa5928584b 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -44,6 +44,7 @@ enum { SCE_KERNEL_ERROR_BAD_FILE = 0x80000209, SCE_KERNEL_ERROR_ACCESS_ERROR = 0x8000020D, + SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002, SCE_KERNEL_ERROR_ERRNO_ARG_LIST_TOO_LONG = 0x80010007, SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_DESCRIPTOR = 0x80010009, SCE_KERNEL_ERROR_ERRNO_RESOURCE_UNAVAILABLE = 0x8001000B, diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 82da25de65..fe5cd6acf5 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1811,8 +1811,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) { s64 size = (s64)info.size; if (!info.exists) { - const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; - const u32 error = hleLogError(LOADER, ERROR_ERRNO_FILE_NOT_FOUND, "file does not exist"); + const u32 error = hleLogError(LOADER, SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "file does not exist"); return hleDelayResult(error, "module loaded", 500); } From ed52d9f9fb50cb83f7e8d56c7967a55bec7b76c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 6 May 2018 21:48:52 +0200 Subject: [PATCH 026/301] Shader depal (VUlkan): Apply a half-texel offset when bilinear filtering, fixing #11008 Also skip three texture samples if all indices are equal. --- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 42 ++++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index c00c35082b..a5cafe4263 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -175,29 +175,37 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { doTextureProjection = false; } - if (doTextureProjection) { - WRITE(p, " vec4 t = textureProj(tex, %s);\n", texcoord); - if (shaderDepal) { - WRITE(p, " vec4 t1 = textureProjOffset(tex, %s, ivec2(1, 0));\n", texcoord); - WRITE(p, " vec4 t2 = textureProjOffset(tex, %s, ivec2(0, 1));\n", texcoord); - WRITE(p, " vec4 t3 = textureProjOffset(tex, %s, ivec2(1, 1));\n", texcoord); + if (!shaderDepal) { + if (doTextureProjection) { + WRITE(p, " vec4 t = textureProj(tex, %s);\n", texcoord); + } else { + WRITE(p, " vec4 t = texture(tex, %s.xy);\n", texcoord); } } else { - WRITE(p, " vec4 t = texture(tex, %s.xy);\n", texcoord); - if (shaderDepal) { - WRITE(p, " vec4 t1 = textureOffset(tex, %s.xy, ivec2(1, 0));\n", texcoord); - WRITE(p, " vec4 t2 = textureOffset(tex, %s.xy, ivec2(0, 1));\n", texcoord); - WRITE(p, " vec4 t3 = textureOffset(tex, %s.xy, ivec2(1, 1));\n", texcoord); + if (doTextureProjection) { + // We don't use textureProj because we need better control and it's probably not much of a savings anyway. + WRITE(p, " vec2 uv = %s.xy/%s.z;\n vec2 uv_round;\n", texcoord, texcoord); + } else { + WRITE(p, " vec2 uv = %s.xy;\n vec2 uv_round;\n", texcoord); } - } - - if (shaderDepal) { + WRITE(p, " vec2 tsize = textureSize(tex, 0);\n"); + WRITE(p, " vec2 fraction;\n"); + WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) != 0;\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " uv_round = uv * tsize - vec2(0.5, 0.5);\n"); + WRITE(p, " fraction = fract(uv_round);\n"); + WRITE(p, " uv_round = (floor(uv_round) + vec2(0.5, 0.5)) / tsize;\n"); // We want to take our four point samples at pixel centers. + WRITE(p, " } else {\n"); + WRITE(p, " uv_round = uv;\n"); + WRITE(p, " }\n"); + WRITE(p, " vec4 t = texture(tex, uv_round);\n"); + WRITE(p, " vec4 t1 = textureOffset(tex, uv_round, ivec2(1, 0));\n"); + WRITE(p, " vec4 t2 = textureOffset(tex, uv_round, ivec2(0, 1));\n"); + WRITE(p, " vec4 t3 = textureOffset(tex, uv_round, ivec2(1, 1));\n"); WRITE(p, " uint depalMask = (base.depal_mask_shift_off_fmt & 0xFF);\n"); WRITE(p, " uint depalShift = (base.depal_mask_shift_off_fmt >> 8) & 0xFF;\n"); WRITE(p, " uint depalOffset = ((base.depal_mask_shift_off_fmt >> 16) & 0xFF) << 4;\n"); WRITE(p, " uint depalFmt = (base.depal_mask_shift_off_fmt >> 24) & 0x3;\n"); - WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) != 0;\n"); - WRITE(p, " vec2 fraction = fract(%s.xy * vec2(textureSize(tex, 0).xy));\n", texcoord); WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n"); WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. WRITE(p, " case 0:\n"); // 565 @@ -251,7 +259,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " };\n"); WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n"); - WRITE(p, " if (bilinear) {\n"); + WRITE(p, " if (bilinear && !(index0 == index1 && index1 == index2 && index2 == index3)) {\n"); WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n"); From b64841e8cc5fd2951ab6569ea8b5d1b6f16d6f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 7 May 2018 00:05:35 +0200 Subject: [PATCH 027/301] Minor optimization --- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index a5cafe4263..1c88f8abc4 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -194,7 +194,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " if (bilinear) {\n"); WRITE(p, " uv_round = uv * tsize - vec2(0.5, 0.5);\n"); WRITE(p, " fraction = fract(uv_round);\n"); - WRITE(p, " uv_round = (floor(uv_round) + vec2(0.5, 0.5)) / tsize;\n"); // We want to take our four point samples at pixel centers. + WRITE(p, " uv_round = (uv_round - fraction + vec2(0.5, 0.5)) / tsize;\n"); // We want to take our four point samples at pixel centers. WRITE(p, " } else {\n"); WRITE(p, " uv_round = uv;\n"); WRITE(p, " }\n"); From ecee4c74b30a535409c104252761c37afba154fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 8 May 2018 18:23:46 +0200 Subject: [PATCH 028/301] Update to the current gradle version, 4.4 --- android/build.gradle | 2 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 59fe1ec9b7..183f19d423 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,7 +35,7 @@ android { } } compileSdkVersion 26 - buildToolsVersion '27.0.0' + buildToolsVersion '27.0.3' defaultConfig { applicationId 'org.ppsspp.ppsspp' if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) { diff --git a/build.gradle b/build.gradle index 277c10f09b..b6cb00b4fc 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.2' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1f02b5e381..a54cb8423f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Oct 26 10:42:05 CEST 2017 +#Tue May 08 18:23:21 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip From 7d3efdc237026f3b2d10cfd1146cfe59e0446d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 8 May 2018 20:45:12 +0200 Subject: [PATCH 029/301] Call updateSustainedPerformanceMode on app focus changes. Should help #11018 --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index b524887241..3f1b07e2a5 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -561,6 +561,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C holder.setFixedSize(desiredSize.x, desiredSize.y); } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + updateSustainedPerformanceMode(); + } + @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.v(TAG, "surfaceChanged: isCreating:" + holder.isCreating() + " holder: " + holder.toString()); From a8083b96845c81ca035f618c195a03fb9abebde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 8 May 2018 23:05:09 +0200 Subject: [PATCH 030/301] Android: Only keep the screen bright ingame. --- Core/System.cpp | 11 +++++++++++ android/src/org/ppsspp/ppsspp/NativeActivity.java | 12 +++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Core/System.cpp b/Core/System.cpp index cc559308c4..777804999f 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -30,6 +30,7 @@ #include #include "base/timeutil.h" +#include "base/NativeApp.h" #include "math/math_util.h" #include "thread/threadutil.h" #include "util/text/utf8.h" @@ -106,6 +107,16 @@ void UpdateUIState(GlobalUIState newState) { if (globalUIState != newState && globalUIState != UISTATE_EXIT) { globalUIState = newState; host->UpdateDisassembly(); + const char *state = nullptr; + switch (globalUIState) { + case UISTATE_EXIT: state = "exit"; break; + case UISTATE_INGAME: state = "ingame"; break; + case UISTATE_MENU: state = "menu"; break; + case UISTATE_PAUSEMENU: state = "pausemenu"; break; + } + if (state) { + System_SendMessage("uistate", state); + } } } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index b524887241..b43328a7b1 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -476,9 +476,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateScreenRotation("onCreate"); updateSustainedPerformanceMode(); - // Keep the screen bright - very annoying if it goes dark when tilting away - Window window = this.getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setVolumeControlStream(AudioManager.STREAM_MUSIC); gainAudioFocus(this.audioManager, this.audioFocusChangeListener); @@ -1290,6 +1287,15 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } else if (mCameraHelper != null && params.equals("stopVideo")) { mCameraHelper.stopCamera(); } + } else if (command.equals("uistate")) { + Window window = this.getWindow(); + if (params.equals("ingame")) { + // Keep the screen bright - very annoying if it goes dark when tilting away + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + // Only keep the screen bright ingame. + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } } return false; } From 3c698a5ddb798c3c4a5e762c600a3553138da43c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 8 May 2018 18:25:15 -0700 Subject: [PATCH 031/301] Io: Prevent error when file locked for writing. When reopening with file sharing flags fixed it, we shouldn't set error. That would cause the open to still fail. --- Core/FileSystems/DirectoryFileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 00177fae73..71e91d06d8 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -252,7 +252,7 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil I18NCategory *err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Disk full while writing data")); error = SCE_KERNEL_ERROR_ERRNO_NO_PERM; - } else { + } else if (!success) { error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; } } From d33b8643cb69a4471a1c25f75367820e0ca4618b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 8 May 2018 18:32:25 -0700 Subject: [PATCH 032/301] Display: Prevent delaying flips inside interrupts. It causes us to run out of idle threads, and doesn't even make sense. --- Core/HLE/sceDisplay.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 8c7fd13597..42c8bd6310 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -937,7 +937,8 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) __DisplaySetFramebuf(topaddr, linesize, pixelformat, sync); - if (delayCycles > 0) { + // No delaying while inside an interrupt. It'll cause idle threads to starve. + if (delayCycles > 0 && !__IsInInterrupt()) { // Okay, the game is going at too high a frame rate. God of War and Fat Princess both do this. // Simply eating the cycles works and is fast, but breaks other games (like Jeanne d'Arc.) // So, instead, we delay this HLE thread only (a small deviation from correct behavior.) From 3f4bfddafc63fdedd34a4d727a4907ac730eb841 Mon Sep 17 00:00:00 2001 From: vnctdj Date: Wed, 9 May 2018 21:22:47 +0200 Subject: [PATCH 033/301] Allow showing version only for SDL --- SDL/SDLMain.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 5e814d05dc..6517748c58 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -448,6 +448,13 @@ static void EmuThreadJoin() { #undef main #endif int main(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--version")) { + printf("%s\n", PPSSPP_GIT_VERSION); + return 0; + } + } + glslang::InitializeProcess(); #if PPSSPP_PLATFORM(RPI) From 9db485b98d035f07f9a6ea2c5019b135453d8342 Mon Sep 17 00:00:00 2001 From: vnctdj Date: Wed, 9 May 2018 21:23:04 +0200 Subject: [PATCH 034/301] Allow showing version only for Qt --- Qt/QtMain.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 5112c20435..395af8c5bb 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -491,6 +491,13 @@ Q_DECL_EXPORT #endif int main(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--version")) { + printf("%s\n", PPSSPP_GIT_VERSION); + return 0; + } + } + glslang::InitializeProcess(); #if defined(Q_OS_LINUX) QApplication::setAttribute(Qt::AA_X11InitThreads, true); From 701c2c02945e574bd72d24a3c2a2859e595edc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 9 May 2018 22:03:45 +0200 Subject: [PATCH 035/301] Call updateSustainedPerformanceMode from surfaceChanged as well. May help #11018 --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 76f9b3c510..95279342cb 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -584,6 +584,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C ensureRenderLoop(); } } + updateSustainedPerformanceMode(); } @Override From 3c066de15844f8a46b01f021acc626451c43f2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 10 May 2018 00:59:21 +0200 Subject: [PATCH 036/301] Win32: Not really appropriate to call exit() on a bad filename. Fixes #11030 --- UI/NativeApp.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 2dd663f5f0..24277f9759 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -500,12 +500,22 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch std::unique_ptr fileLoader(ConstructFileLoader(boot_filename)); if (!fileLoader->Exists()) { fprintf(stderr, "File not found: %s\n", boot_filename.c_str()); +#ifdef _WIN32 + // Ignore and proceed. +#else + // Bail. exit(1); +#endif } } } else { fprintf(stderr, "Can only boot one file"); +#ifdef _WIN32 + // Ignore and proceed. +#else + // Bail. exit(1); +#endif } } } From 3976b03bcd5c846cff779af4976def136f479374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 10 May 2018 19:32:28 +0200 Subject: [PATCH 037/301] Create vital directories on Android storage permission grant. Should help #11020 --- Core/System.cpp | 4 ---- UI/NativeApp.cpp | 27 +++++++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Core/System.cpp b/Core/System.cpp index 777804999f..a7a96c713f 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -564,10 +564,6 @@ void InitSysDirectories() { File::CreateDir(g_Config.memStickDirectory + "PSP/GAME"); File::CreateDir(g_Config.memStickDirectory + "PSP/SAVEDATA"); File::CreateDir(g_Config.memStickDirectory + "PSP/PPSSPP_STATE"); -#ifdef ANDROID - // Avoid media scanners in PPSSPP_STATE directory - File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/PPSSPP_STATE/.nomedia"); -#endif if (g_Config.currentDirectory.empty()) { g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 24277f9759..5662d5c024 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -326,6 +326,19 @@ static void PostLoadConfig() { i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath); } +void CreateDirectoriesAndroid() { + // On Android, create a PSP directory tree in the external_dir, + // to hopefully reduce confusion a bit. + ILOG("Creating %s", (g_Config.memStickDirectory + "PSP").c_str()); + File::CreateDir((g_Config.memStickDirectory + "PSP").c_str()); + File::CreateDir((g_Config.memStickDirectory + "PSP/SAVEDATA").c_str()); + File::CreateDir((g_Config.memStickDirectory + "PSP/PPSSPP_STATE").c_str()); + File::CreateDir((g_Config.memStickDirectory + "PSP/GAME").c_str()); + // Avoid media scanners in PPSSPP_STATE and SAVEDATA directories + File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/PPSSPP_STATE/.nomedia"); + File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia"); +} + void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir, bool fs) { net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times. @@ -409,17 +422,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch LogManager *logman = LogManager::GetInstance(); #ifdef __ANDROID__ - // TODO: This is also done elsewhere. Remove? - // On Android, create a PSP directory tree in the external_dir, - // to hopefully reduce confusion a bit. - ILOG("Creating %s", (g_Config.memStickDirectory + "PSP").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP/SAVEDATA").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP/GAME").c_str()); + CreateDirectoriesAndroid(); #endif - - const char *fileToLog = 0; const char *stateToLoad = 0; @@ -963,10 +968,12 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) { osm.Show(sy->T("WARNING: Battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving"); #endif } - Core_SetPowerSaving(value != "false"); } if (msg == "permission_granted" && value == "storage") { +#ifdef __ANDROID__ + CreateDirectoriesAndroid(); +#endif // We must have failed to load the config before, so load it now to avoid overwriting the old config // with a freshly generated one. ILOG("Reloading config after storage permission grant."); From 775a25e5309db4065726aba5014ec2bb2fa8e949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 10 May 2018 20:52:21 +0200 Subject: [PATCH 038/301] Another directory, plus no need to use c_str --- UI/NativeApp.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 5662d5c024..c822702b49 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -330,10 +330,12 @@ void CreateDirectoriesAndroid() { // On Android, create a PSP directory tree in the external_dir, // to hopefully reduce confusion a bit. ILOG("Creating %s", (g_Config.memStickDirectory + "PSP").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP/SAVEDATA").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP/PPSSPP_STATE").c_str()); - File::CreateDir((g_Config.memStickDirectory + "PSP/GAME").c_str()); + File::CreateDir(g_Config.memStickDirectory + "PSP"); + File::CreateDir(g_Config.memStickDirectory + "PSP/SAVEDATA"); + File::CreateDir(g_Config.memStickDirectory + "PSP/PPSSPP_STATE"); + File::CreateDir(g_Config.memStickDirectory + "PSP/GAME"); + File::CreateDir(g_Config.memStickDirectory + "PSP/SYSTEM"); + // Avoid media scanners in PPSSPP_STATE and SAVEDATA directories File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/PPSSPP_STATE/.nomedia"); File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia"); From ef67899fdc7aa9abed523465884f9e2525ef1b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 10 May 2018 21:20:44 +0200 Subject: [PATCH 039/301] Let's try to update the sustainedperformancemode in yet another place... See #11018 --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 95279342cb..b4257dcd45 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -727,6 +727,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C @Override protected void onResume() { super.onResume(); + updateSustainedPerformanceMode(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } From 011e57c0e7c0c9a09ff68c74b5bfb96a011ccaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 13 May 2018 20:35:10 +0200 Subject: [PATCH 040/301] Always specify GLSL version 450 when compiling Vulkan shaders. Bit of a risky change so late in the game but it's wrong, it needs fixing. See for example the comments to https://github.com/hrydgard/ppsspp/commit/fb7a63bd11ba785209babfe9631956110d46c9bf --- GPU/Common/DepalettizeShaderCommon.cpp | 2 +- GPU/Vulkan/DepalettizeShaderVulkan.cpp | 2 +- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 2 +- GPU/Vulkan/FramebufferVulkan.cpp | 4 ++-- GPU/Vulkan/ShaderManagerVulkan.cpp | 2 +- GPU/Vulkan/StencilBufferVulkan.cpp | 4 ++-- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 2 +- ext/native/thin3d/thin3d.cpp | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index c65db54599..5482ae025d 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -38,7 +38,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang WRITE(p, "Texture2D tex : register(t0);\n"); WRITE(p, "Texture2D pal : register(t3);\n"); } else if (language == GLSL_VULKAN) { - WRITE(p, "#version 140\n"); + WRITE(p, "#version 450\n"); WRITE(p, "#extension GL_ARB_separate_shader_objects : enable\n"); WRITE(p, "#extension GL_ARB_shading_language_420pack : enable\n"); WRITE(p, "layout(set = 0, binding = 0) uniform sampler2D tex;\n"); diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index 1cc701a28f..57e61fef65 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -22,7 +22,7 @@ #include "GPU/Vulkan/VulkanUtil.h" #include "Common/Vulkan/VulkanImage.h" -static const char depal_vs[] = R"(#version 400 +static const char depal_vs[] = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable layout (location = 0) in vec3 a_position; diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 1c88f8abc4..753bee977c 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -34,7 +34,7 @@ #include "GPU/GPUState.h" static const char *vulkan_glsl_preamble = - "#version 400\n" + "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n\n"; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index e731802691..c05a667d2a 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -56,7 +56,7 @@ #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/VulkanUtil.h" -static const char tex_fs[] = R"(#version 400 +static const char tex_fs[] = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable layout (binding = 0) uniform sampler2D sampler0; @@ -67,7 +67,7 @@ void main() { } )"; -static const char tex_vs[] = R"(#version 400 +static const char tex_vs[] = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable layout (location = 0) in vec3 a_position; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index c7f93ed540..2168eae5b1 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -358,7 +358,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 13 +#define CACHE_VERSION 14 struct VulkanCacheHeader { uint32_t magic; uint32_t version; diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index febe32c61c..35661711d5 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -27,7 +27,7 @@ #include "GPU/Vulkan/TextureCacheVulkan.h" #include "GPU/Vulkan/VulkanUtil.h" -static const char *stencil_fs = R"(#version 400 +static const char *stencil_fs = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable layout (binding = 0) uniform sampler2D tex; @@ -50,7 +50,7 @@ void main() { } )"; -static const char stencil_vs[] = R"(#version 400 +static const char stencil_vs[] = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable layout (location = 0) out vec2 v_texcoord0; diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index b4a22977fa..2d05673e86 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -37,7 +37,7 @@ #include "GPU/Vulkan/ShaderManagerVulkan.h" static const char *vulkan_glsl_preamble = -"#version 430\n" +"#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n\n"; diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index c475af1513..6f4eafb3bf 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -227,7 +227,7 @@ static const std::vector vsCol = { "}\n" }, { ShaderLanguage::GLSL_VULKAN, - "#version 400\n" + "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n" "layout (std140, set = 0, binding = 0) uniform bufferVals {\n" @@ -293,7 +293,7 @@ static const std::vector vsTexCol = { "}\n" }, { ShaderLanguage::GLSL_VULKAN, - "#version 400\n" + "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" "#extension GL_ARB_shading_language_420pack : enable\n" "layout (std140, set = 0, binding = 0) uniform bufferVals {\n" From 4ba9fe3e0e35e1b4a993b478aac3a3170e925e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 13 May 2018 20:51:20 +0200 Subject: [PATCH 041/301] GLES depal (currently disabled): Use high int precision. Should fix issue mentioned in https://github.com/hrydgard/ppsspp/commit/fb7a63bd11ba785209babfe9631956110d46c9bf --- GPU/GLES/FragmentShaderGeneratorGLES.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 421d8a9f92..16ea72da39 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -174,6 +174,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform bool isModeClear = id.Bit(FS_BIT_CLEARMODE); + if (shaderDepal) { + WRITE(p, "precision highp int;\n"); + } + const char *shading = ""; if (glslES30) shading = doFlatShading ? "flat" : ""; From 061f92c17795c9d91daf7867f5fe797c009fea64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 14 May 2018 21:19:48 +0200 Subject: [PATCH 042/301] Minor code simplification --- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 753bee977c..ab919a0cad 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -233,26 +233,26 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " }\n"); WRITE(p, " break;\n"); WRITE(p, " case 2:\n"); // 4444 - WRITE(p, " col = uvec4(t.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " col = uvec4(t.rgba * 15.99);\n"); WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); WRITE(p, " if (bilinear) {\n"); - WRITE(p, " col = uvec4(t1.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " col = uvec4(t1.rgba * 15.99);\n"); WRITE(p, " index1 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); - WRITE(p, " col = uvec4(t2.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " col = uvec4(t2.rgba * 15.99);\n"); WRITE(p, " index2 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); - WRITE(p, " col = uvec4(t3.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " col = uvec4(t3.rgba * 15.99);\n"); WRITE(p, " index3 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); WRITE(p, " }\n"); WRITE(p, " break;\n"); WRITE(p, " case 3:\n"); // 8888 - WRITE(p, " col = uvec4(t.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " col = uvec4(t.rgba * 255.99);\n"); WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); WRITE(p, " if (bilinear) {\n"); - WRITE(p, " col = uvec4(t1.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " col = uvec4(t1.rgba * 255.99);\n"); WRITE(p, " index1 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); - WRITE(p, " col = uvec4(t2.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " col = uvec4(t2.rgba * 255.99);\n"); WRITE(p, " index2 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); - WRITE(p, " col = uvec4(t3.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " col = uvec4(t3.rgba * 255.99);\n"); WRITE(p, " index3 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); WRITE(p, " }\n"); WRITE(p, " break;\n"); From ab65c5210ca3bebf621d09a4a778b408413a0f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 14 May 2018 21:20:51 +0200 Subject: [PATCH 043/301] GLES shader depal fix. Again, this is disabled by default so should not affect anything. --- GPU/GLES/FragmentShaderGeneratorGLES.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 16ea72da39..20cfebbfe5 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -174,7 +174,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform bool isModeClear = id.Bit(FS_BIT_CLEARMODE); - if (shaderDepal) { + if (shaderDepal && gl_extensions.IsGLES) { WRITE(p, "precision highp int;\n"); } From 9b6dee126df136fb88acdd65abdb18fcad74cced Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 May 2018 14:15:09 -0700 Subject: [PATCH 044/301] Core: Shutdown properly on load failure. --- Core/System.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Core/System.cpp b/Core/System.cpp index 777804999f..7d467be28e 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -351,13 +351,17 @@ bool PSP_InitUpdate(std::string *error_string) { PSP_SetLoading("Starting graphics..."); success = GPU_Init(coreParameter.graphicsContext, coreParameter.thin3d); if (!success) { - PSP_Shutdown(); *error_string = "Unable to initialize rendering engine."; } } - pspIsInited = success && GPU_IsReady(); - pspIsIniting = success && !pspIsInited; - return !success || pspIsInited; + if (!success) { + PSP_Shutdown(); + return true; + } + + pspIsInited = GPU_IsReady(); + pspIsIniting = !pspIsInited; + return pspIsInited; } bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) { From 037a20e7c739b51ff94cc374a54c1cfeff7f76ce Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 May 2018 20:51:29 -0700 Subject: [PATCH 045/301] SaveState: Fix rename when undo disabled. On Windows, for example, the rename target cannot exist. This also fixes the screenshot rename for the undo. --- Core/SaveState.cpp | 48 ++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index f999024a39..30889f8e71 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -423,6 +423,28 @@ namespace SaveState } } + static void DeleteIfExists(const std::string &fn) { + // Just avoiding error messages. + if (File::Exists(fn)) { + File::Delete(fn); + } + } + + static void RenameIfExists(const std::string &from, const std::string &to) { + if (File::Exists(from)) { + File::Rename(from, to); + } + } + + static void SwapIfExists(const std::string &from, const std::string &to) { + std::string temp = from + ".tmp"; + if (File::Exists(from)) { + File::Rename(from, temp); + File::Rename(to, from); + File::Rename(temp, to); + } + } + void SaveSlot(const std::string &gameFilename, int slot, Callback callback, void *cbUserData) { std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); @@ -432,11 +454,11 @@ namespace SaveState if (!fn.empty()) { auto renameCallback = [=](bool status, const std::string &message, void *data) { if (status) { - if (File::Exists(fnUndo) && g_Config.bEnableStateUndo) { - File::Delete(fnUndo); - } - if (File::Exists(fn) && g_Config.bEnableStateUndo) { - File::Rename(fn, fnUndo); + if (g_Config.bEnableStateUndo) { + DeleteIfExists(fnUndo); + RenameIfExists(fn, fnUndo); + } else { + DeleteIfExists(fn); } File::Rename(fn + ".tmp", fn); } @@ -445,8 +467,9 @@ namespace SaveState } }; // Let's also create a screenshot. - if (File::Exists(shot) && g_Config.bEnableStateUndo) { - File::Rename(shot, shotUndo); + if (g_Config.bEnableStateUndo) { + DeleteIfExists(shotUndo); + RenameIfExists(shot, shotUndo); } SaveScreenshot(shot, Callback(), 0); Save(fn + ".tmp", renameCallback, cbUserData); @@ -466,15 +489,8 @@ namespace SaveState // Do nothing if there's no undo. if (File::Exists(fnUndo)) { // Swap them so they can undo again to redo. Mistakes happen. - if (File::Exists(shotUndo)) { - File::Rename(shot, shot + ".tmp"); - File::Rename(shotUndo, shot); - File::Rename(shot + ".tmp", shotUndo); - } - - File::Rename(fn, fn + ".tmp"); - File::Rename(fnUndo, fn); - File::Rename(fn + ".tmp", fnUndo); + SwapIfExists(shotUndo, shot); + SwapIfExists(fnUndo, fn); return true; } From 0e0a3d28049e778e20f81f94fcbf1347ec971546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 25 May 2018 22:42:43 +0200 Subject: [PATCH 046/301] Android: In v21 and later, make the navigation bar black using styles.xml --- android/AndroidManifest.xml | 2 +- android/res/values-v21/styles.xml | 9 +++++++++ android/res/values/styles.xml | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 android/res/values-v21/styles.xml create mode 100644 android/res/values/styles.xml diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index cc2938f5c5..4fe2a15410 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -47,7 +47,7 @@ android:name=".PpssppActivity" android:configChanges="locale|keyboard|keyboardHidden|navigation|uiMode" android:label="@string/app_name" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + android:theme="@style/ppsspp_style"> diff --git a/android/res/values-v21/styles.xml b/android/res/values-v21/styles.xml new file mode 100644 index 0000000000..31ce1cc3d3 --- /dev/null +++ b/android/res/values-v21/styles.xml @@ -0,0 +1,9 @@ + + + #FF000000 + + diff --git a/android/res/values/styles.xml b/android/res/values/styles.xml new file mode 100644 index 0000000000..dfa2fc94ac --- /dev/null +++ b/android/res/values/styles.xml @@ -0,0 +1,8 @@ + + + #FF000000 + + From 7c921c5367da52acd47359b73e6ad77654822ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 25 May 2018 23:40:34 +0200 Subject: [PATCH 047/301] Android immersive mode: Fix display size stretch and touch screen inaccuracy when toggling immersive mode --- .../src/org/ppsspp/ppsspp/NativeActivity.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index b4257dcd45..7f301c992c 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -415,6 +415,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } else { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring"); } + updateDisplayMeasurements(); } // Need API 11 to check for existence of a vibrator? Zany. @@ -450,22 +451,34 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C sz.y = NativeApp.getDesiredBackbufferHeight(); } + @TargetApi(17) + public void updateDisplayMeasurements() { + Display display = getWindowManager().getDefaultDisplay(); + + + DisplayMetrics metrics = new DisplayMetrics(); + if (useImmersive() && Build.VERSION.SDK_INT >= 17) { + display.getRealMetrics(metrics); + } else { + display.getMetrics(metrics); + } + densityDpi = metrics.densityDpi; + refreshRate = display.getRefreshRate(); + + NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int)densityDpi, refreshRate); + } + @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextRenderer.init(this); shuttingDown = false; registerCallbacks(); - Display display = getWindowManager().getDefaultDisplay(); - DisplayMetrics metrics = new DisplayMetrics(); - display.getMetrics(metrics); - densityDpi = metrics.densityDpi; - refreshRate = display.getRefreshRate(); - - // Set early to be able to set defaults when loading config for the first time. Like figuring out + // This calls NativeApp.setDisplayParameters. Make sure that's done early in order + // to be able to set defaults when loading config for the first time. Like figuring out // whether to start at 1x or 2x. - NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int)densityDpi, refreshRate); + updateDisplayMeasurements(); if (!initialized) { Initialize(); From e8da82c011d7c12fc0da3180d74492d30d73a43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 26 May 2018 14:20:48 +0200 Subject: [PATCH 048/301] Update lang --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 8ea7353697..c2c4ad9c38 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 8ea7353697cdd3d308676966644b8ef6ba82786e +Subproject commit c2c4ad9c38c5f5e97ff022a703c470fcd53da249 From 09e85eb150c927cd501d0c658dff94ba75ae2b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 26 May 2018 14:21:47 +0200 Subject: [PATCH 049/301] Update version to 1.6 in AndroidManifest.xml --- android/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 4fe2a15410..adbf32f9de 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="16000000" + android:versionName="1.6.0.0"> Date: Sat, 26 May 2018 17:50:37 +0200 Subject: [PATCH 050/301] Check for a valid screen manager in NativeIsAtTopLevel --- UI/NativeApp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c822702b49..1f7ee8509e 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1004,6 +1004,11 @@ void NativeUpdate() { } bool NativeIsAtTopLevel() { + // This might need some synchronization? + if (!screenManager) { + ELOG("No screen manager active"); + return false; + } Screen *currentScreen = screenManager->topScreen(); if (currentScreen) { bool top = currentScreen->isTopLevel(); From d7770307f3074fcac7558856fdb949142548b652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 27 May 2018 21:53:13 +0200 Subject: [PATCH 051/301] Change some log levels to verbose --- Core/HLE/sceIo.cpp | 2 +- Core/HLE/sceUmd.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index f2f5eebf6b..77e1a24113 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -2086,7 +2086,7 @@ static u32 sceIoPollAsync(int id, u32 address) { FileNode *f = __IoGetFd(id, error); if (f) { if (f->pendingAsyncResult) { - DEBUG_LOG(SCEIO, "%lli = sceIoPollAsync(%i, %08x): not ready", f->asyncResult, id, address); + VERBOSE_LOG(SCEIO, "%lli = sceIoPollAsync(%i, %08x): not ready", f->asyncResult, id, address); return 1; } else if (f->hasAsyncResult) { DEBUG_LOG(SCEIO, "%lli = sceIoPollAsync(%i, %08x)", f->asyncResult, id, address); diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index f293a72d2b..bd4cb56d7b 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -342,7 +342,8 @@ static u32 sceUmdGetDriveStat() } //u32 retVal = PSP_UMD_INITED | PSP_UMD_READY | PSP_UMD_PRESENT; u32 retVal = __KernelUmdGetState(); - DEBUG_LOG(SCEIO,"0x%02x=sceUmdGetDriveStat()", retVal); + // This one can be very spammy. + VERBOSE_LOG(SCEIO,"0x%02x=sceUmdGetDriveStat()", retVal); return retVal; } From b730d187e8e1417b86bde56435540fe1f67f49b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 27 May 2018 21:54:07 +0200 Subject: [PATCH 052/301] Add some paranoid checks guided by stack traces from Google Play --- GPU/GLES/DrawEngineGLES.cpp | 9 ++++++--- UI/EmuScreen.cpp | 17 +++++++++++------ android/jni/app-android.cpp | 3 ++- ext/native/thin3d/VulkanQueueRunner.cpp | 4 ++-- ext/native/thin3d/thin3d_vulkan.cpp | 9 +++++++-- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 2241a18dd3..a9769383b9 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -108,10 +108,11 @@ void DrawEngineGLES::DeviceRestore() { } void DrawEngineGLES::InitDeviceObjects() { + _assert_msg_(G3D, render_ != nullptr, "Render manager must be set"); + for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { frameData_[i].pushVertex = render_->CreatePushBuffer(i, GL_ARRAY_BUFFER, 1024 * 1024); frameData_[i].pushIndex = render_->CreatePushBuffer(i, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024); - } int vertexSize = sizeof(TransformedVertex); @@ -129,8 +130,10 @@ void DrawEngineGLES::DestroyDeviceObjects() { if (!frameData_[i].pushVertex && !frameData_[i].pushIndex) continue; - render_->DeletePushBuffer(frameData_[i].pushVertex); - render_->DeletePushBuffer(frameData_[i].pushIndex); + if (frameData_[i].pushVertex) + render_->DeletePushBuffer(frameData_[i].pushVertex); + if (frameData_[i].pushIndex) + render_->DeletePushBuffer(frameData_[i].pushIndex); frameData_[i].pushVertex = nullptr; frameData_[i].pushIndex = nullptr; } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index ffef7dd59f..3e1b322e0d 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -856,13 +856,16 @@ public: // PIC1 is the loading image, so let's only draw if it's available. if (ginfo && ginfo->pic1.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic1.texture->GetTexture()); + Draw::Texture *texture = ginfo->pic1.texture->GetTexture(); + if (texture) { + dc.GetDrawContext()->BindTexture(0, texture); - double loadTime = ginfo->pic1.timeLoaded; - uint32_t color = alphaMul(color_, ease((time_now_d() - loadTime) * 3)); - dc.Draw()->DrawTexRect(dc.GetBounds(), 0, 0, 1, 1, color); - dc.Flush(); - dc.RebindTexture(); + double loadTime = ginfo->pic1.timeLoaded; + uint32_t color = alphaMul(color_, ease((time_now_d() - loadTime) * 3)); + dc.Draw()->DrawTexRect(dc.GetBounds(), 0, 0, 1, 1, color); + dc.Flush(); + dc.RebindTexture(); + } } } @@ -1154,6 +1157,8 @@ void EmuScreen::render() { using namespace Draw; DrawContext *thin3d = screenManager()->getDrawContext(); + if (!thin3d) + return; // shouldn't really happen but I've seen a suspicious stack trace.. if (invalid_) { // Loading, or after shutdown? diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 3043425f61..8ca63aab67 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -650,7 +650,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, if (useCPUThread) { // This is the "GPU thread". - graphicsContext->ThreadFrame(); + if (graphicsContext) + graphicsContext->ThreadFrame(); } else { UpdateRunLoopAndroid(env); } diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 6623112428..f22fa7d340 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -353,8 +353,6 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes // as early as possible in the frame (Wipeout billboards). - // Push down empty "Clear/Store" renderpasses, and merge them with the first "Load/Store" to the same framebuffer. - // Actually let's just bother with the first one for now. This affects Wipeout Pure. for (int j = 0; j < (int)steps.size() - 1; j++) { if (steps[j]->stepType == VKRStepType::RENDER && steps[j]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { @@ -362,6 +360,8 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st steps[j]->render.finalColorLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } + // Push down empty "Clear/Store" renderpasses, and merge them with the first "Load/Store" to the same framebuffer. + // Actually let's just bother with the first one for now. This affects Wipeout Pure. if (steps.size() > 1 && steps[j]->stepType == VKRStepType::RENDER && steps[j]->render.numDraws == 0 && steps[j]->render.color == VKRRenderPassAction::CLEAR && diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 6f6a7dc735..ab9b828def 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -312,8 +312,13 @@ public: } VkImageView GetImageView() { - vkTex_->Touch(); - return vkTex_->GetImageView(); + if (vkTex_) { + vkTex_->Touch(); + return vkTex_->GetImageView(); + } else { + // This would be bad. + return VK_NULL_HANDLE; + } } private: From 343763cf5a2d66c718538a5ac056ad2c9af5c6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 27 May 2018 22:16:36 +0200 Subject: [PATCH 053/301] More paranoia from Google Play --- android/jni/app-android.cpp | 6 +++++- android/src/org/ppsspp/ppsspp/NativeActivity.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 8ca63aab67..9675eba147 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -323,6 +323,8 @@ bool System_GetPropertyBool(SystemProperty prop) { } std::string GetJavaString(JNIEnv *env, jstring jstr) { + if (!jstr) + return ""; const char *str = env->GetStringUTFChars(jstr, 0); std::string cpp_string = std::string(str); env->ReleaseStringUTFChars(jstr, str); @@ -397,7 +399,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init langRegion = GetJavaString(env, jlangRegion); std::string externalDir = GetJavaString(env, jexternalDir); - std::string user_data_path = GetJavaString(env, jdataDir) + "/"; + std::string user_data_path = GetJavaString(env, jdataDir); + if (user_data_path.size() > 0) + user_data_path += "/"; library_path = GetJavaString(env, jlibraryDir) + "/"; std::string shortcut_param = GetJavaString(env, jshortcutParam); std::string cacheDir = GetJavaString(env, jcacheDir); diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 7f301c992c..9888a9fa11 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -296,7 +296,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C File sdcard = Environment.getExternalStorageDirectory(); String externalStorageDir = sdcard.getAbsolutePath(); - String dataDir = this.getFilesDir().getAbsolutePath(); + File filesDir = this.getFilesDir(); + String dataDir = null; + if (filesDir != null) { + // Null has been seen in Google Play stacktraces + dataDir = this.getFilesDir().getAbsolutePath(); + } String apkFilePath = appInfo.sourceDir; String cacheDir = getCacheDir().getAbsolutePath(); From 0d6be3a4768453eb36570b154cc1a9d5dd97dd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 27 May 2018 22:19:03 +0200 Subject: [PATCH 054/301] GLES: Refresh the draw context in DrawEngineGLES on DeviceRestore. How did we miss this? --- GPU/GLES/DrawEngineGLES.cpp | 3 ++- GPU/GLES/DrawEngineGLES.h | 2 +- GPU/GLES/GPU_GLES.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index a9769383b9..72861b4023 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -103,7 +103,8 @@ void DrawEngineGLES::DeviceLost() { DestroyDeviceObjects(); } -void DrawEngineGLES::DeviceRestore() { +void DrawEngineGLES::DeviceRestore(Draw::DrawContext *draw) { + draw_ = draw; InitDeviceObjects(); } diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 535535d42e..2901a1c384 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -130,7 +130,7 @@ public: } void DeviceLost(); - void DeviceRestore(); + void DeviceRestore(Draw::DrawContext *draw); void ClearTrackedVertexArrays() override; void DecimateTrackedVertexArrays(); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index b1505cfacd..0b3e14489d 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -351,7 +351,7 @@ void GPU_GLES::DeviceRestore() { textureCacheGL_->DeviceRestore(draw_); framebufferManagerGL_->DeviceRestore(draw_); - drawEngine_.DeviceRestore(); + drawEngine_.DeviceRestore(draw_); } void GPU_GLES::Reinitialize() { From 14d17419b1f170ea3e2ab10bbb15671af93461e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 27 May 2018 22:51:00 +0200 Subject: [PATCH 055/301] Minor cleanup --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 9888a9fa11..44760b8869 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -300,7 +300,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C String dataDir = null; if (filesDir != null) { // Null has been seen in Google Play stacktraces - dataDir = this.getFilesDir().getAbsolutePath(); + dataDir = filesDir.getAbsolutePath(); } String apkFilePath = appInfo.sourceDir; String cacheDir = getCacheDir().getAbsolutePath(); From 423309da50efd6400b7338a9a9afd7a24db63ecc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 27 May 2018 14:14:02 -0700 Subject: [PATCH 056/301] Vulkan: Don't merge render passes if read from. In the future, we might actually track dependencies so we can smartly sort the render passes instead. See #11079 - screen got brighter because a cleared framebuffer was used as a texture. --- ext/native/thin3d/VulkanQueueRunner.cpp | 2 ++ ext/native/thin3d/VulkanQueueRunner.h | 2 ++ ext/native/thin3d/VulkanRenderManager.cpp | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 6623112428..c1dadb9c44 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -364,6 +364,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st if (steps.size() > 1 && steps[j]->stepType == VKRStepType::RENDER && steps[j]->render.numDraws == 0 && + steps[j]->render.numReads == 0 && steps[j]->render.color == VKRRenderPassAction::CLEAR && steps[j]->render.stencil == VKRRenderPassAction::CLEAR && steps[j]->render.depth == VKRRenderPassAction::CLEAR) { @@ -391,6 +392,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st steps[i]->copy.src == steps[j]->render.framebuffer) { // Can't eliminate the clear if a game copies from it before it's // rendered to. However this should be rare. + // TODO: This should never happen when we check numReads now. break; } } diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 3ff8e161ea..5acfb22d6b 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -122,6 +122,8 @@ struct VKRStep { float clearDepth; int clearStencil; int numDraws; + // Downloads and textures from this pass. + int numReads; VkImageLayout finalColorLayout; } render; struct { diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index a7cec21ed8..325d0f6851 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -422,6 +422,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR step->render.clearDepth = clearDepth; step->render.clearStencil = clearStencil; step->render.numDraws = 0; + step->render.numReads = 0; step->render.finalColorLayout = !fb ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; steps_.push_back(step); @@ -431,6 +432,13 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR } bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { + for (int i = (int)steps_.size() - 1; i >= 0; i--) { + if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == src) { + steps_[i]->render.numReads++; + break; + } + } + VKRStep *step = new VKRStep{ VKRStepType::READBACK }; step->readback.aspectMask = aspectBits; step->readback.src = src; @@ -720,6 +728,7 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; } + steps_[i]->render.numReads++; break; } } @@ -762,6 +771,13 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, _dbg_assert_msg_(G3D, dstRect.extent.width > 0, "blit dstwidth == 0"); _dbg_assert_msg_(G3D, dstRect.extent.height > 0, "blit dstheight == 0"); + for (int i = (int)steps_.size() - 1; i >= 0; i--) { + if (steps_[i]->stepType == VKRStepType::RENDER && steps_[i]->render.framebuffer == src) { + steps_[i]->render.numReads++; + break; + } + } + VKRStep *step = new VKRStep{ VKRStepType::BLIT }; step->blit.aspectMask = aspectMask; @@ -784,6 +800,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in if (steps_[i]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) { steps_[i]->render.finalColorLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } + steps_[i]->render.numReads++; break; } } From e7d07aff71389dd67791346476c03b6a5e52c665 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 27 May 2018 22:28:09 -0700 Subject: [PATCH 057/301] Vulkan: Update shaders/state when dirty properly. Can't have been right before. --- GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 7a5eeb3f0c..f77a813837 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -801,7 +801,7 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } - if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { + if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true); // usehwtransform _dbg_assert_msg_(G3D, vshader->UseHWTransform(), "Bad vshader"); From c588046d99fac00f73f7be4fc7b233c9cd5fb198 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 3 May 2018 23:11:18 -0700 Subject: [PATCH 058/301] Savedata: Respect secureVersion parameter. This isn't used in many games, but see #10814. --- Core/Dialog/SavedataParam.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 1c4f27f99e..9c33e4e04e 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -611,7 +611,9 @@ bool SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::stri int SavedataParam::DetermineCryptMode(const SceUtilitySavedataParam *param) const { int decryptMode = 1; - if (HasKey(param)) { + if (param->secureVersion != 0) { + decryptMode = param->secureVersion; + } else if (HasKey(param)) { decryptMode = GetSDKMainVersion(sceKernelGetCompiledSdkVersion()) >= 4 ? 5 : 3; } return decryptMode; @@ -623,19 +625,18 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 u8 *cryptKey = new u8[0x10]; memset(cryptKey, 0, 0x10); - bool hasKey = HasKey(param); + int decryptMode = DetermineCryptMode(param); + bool hasKey = decryptMode > 1; if (hasKey) { memcpy(cryptKey, param->key, 0x10); } memset(data_base + saveSize, 0, align_len - saveSize); memcpy(data_base, saveData, saveSize); - int decryptMode = DetermineCryptMode(param); if (decryptMode != prevCryptMode) { if (prevCryptMode == 1 && param->key[0] == 0) { // Backwards compat for a bug we used to have. WARN_LOG(SCEUTILITY, "Savedata loading with hashmode %d instead of detected %d", prevCryptMode, decryptMode); - hasKey = false; decryptMode = prevCryptMode; // Don't notify the user if we're not going to upgrade the save. @@ -657,6 +658,7 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 host->NotifyUserMessage(di->T("Old savedata detected"), 6.0f); } } + hasKey = decryptMode > 1; } if (DecryptSave(decryptMode, data_base, &saveSize, &align_len, (hasKey?cryptKey:0)) == 0) { From 43b18578f401458896538c6551f69aaf189cd471 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 May 2018 21:28:57 -0700 Subject: [PATCH 059/301] Update default firmware version to 6.60. Not many games call sceKernelDevkitVersion(), but we mostly match 6.60. See also: https://report.ppsspp.org/logs/kind/883?status=any --- Core/Config.h | 2 +- Core/HLE/sceKernel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Config.h b/Core/Config.h index 466624ce67..a08551468d 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -27,7 +27,7 @@ extern const char *PPSSPP_GIT_VERSION; const int PSP_MODEL_FAT = 0; const int PSP_MODEL_SLIM = 1; -const int PSP_DEFAULT_FIRMWARE = 150; +const int PSP_DEFAULT_FIRMWARE = 660; static const s8 VOLUME_OFF = 0; static const s8 VOLUME_MAX = 10; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 1db8b5d60d..bf409a247c 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -313,7 +313,7 @@ u32 sceKernelDevkitVersion() int revision = firmwareVersion % 10; int devkitVersion = (major << 24) | (minor << 16) | (revision << 8) | 0x10; - DEBUG_LOG_REPORT_ONCE(devkitVer, SCEKERNEL, "%08x=sceKernelDevkitVersion()", devkitVersion); + DEBUG_LOG(SCEKERNEL, "%08x=sceKernelDevkitVersion()", devkitVersion); return devkitVersion; } From 88e358afc82d144fc066a3c59b7e7001e7196618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 28 May 2018 18:47:10 +0200 Subject: [PATCH 060/301] Update to 1.6.1 --- README.md | 5 +++++ android/AndroidManifest.xml | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a5ec913e4..e4651a678e 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,11 @@ For build instructions and other development tutorials, see [the wiki](https://g For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games). +What's new in 1.6.1 +------------------- +* Crashfixes +* Fix broken graphics in flOw. + What's new in 1.6.0 ------------------- * OpenGL backend now properly multithreaded, giving a good speed boost. diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index adbf32f9de..256df60c9b 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="16010000" + android:versionName="1.6.1.0"> Date: Tue, 29 May 2018 23:07:22 +0200 Subject: [PATCH 061/301] GLES: Properly restore the pointer to the render manager in more places --- GPU/GLES/DepalettizeShaderGLES.cpp | 4 ++++ GPU/GLES/DepalettizeShaderGLES.h | 5 +++++ GPU/GLES/FragmentTestCacheGLES.cpp | 4 ++++ GPU/GLES/FragmentTestCacheGLES.h | 4 ++++ GPU/GLES/GPU_GLES.cpp | 12 +++++++----- GPU/GLES/ShaderManagerGLES.cpp | 13 +++++++++++-- GPU/GLES/ShaderManagerGLES.h | 5 ++++- 7 files changed, 39 insertions(+), 8 deletions(-) diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index 77614c93df..f2b32c7678 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -70,6 +70,10 @@ DepalShaderCacheGLES::~DepalShaderCacheGLES() { Clear(); } +void DepalShaderCacheGLES::DeviceRestore(Draw::DrawContext *draw) { + render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); +} + bool DepalShaderCacheGLES::CreateVertexShader() { std::string src(useGL3_ ? depalVShader300 : depalVShader100); vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, src, "depal"); diff --git a/GPU/GLES/DepalettizeShaderGLES.h b/GPU/GLES/DepalettizeShaderGLES.h index 6fd9749fec..e183ba22b0 100644 --- a/GPU/GLES/DepalettizeShaderGLES.h +++ b/GPU/GLES/DepalettizeShaderGLES.h @@ -54,6 +54,11 @@ public: std::vector DebugGetShaderIDs(DebugShaderType type); std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); + void DeviceLost() { + Clear(); + } + void DeviceRestore(Draw::DrawContext *draw); + private: bool CreateVertexShader(); diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index 0ad098a499..ea482896a7 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -35,6 +35,10 @@ FragmentTestCacheGLES::~FragmentTestCacheGLES() { Clear(); } +void FragmentTestCacheGLES::DeviceRestore(Draw::DrawContext *draw) { + render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); +} + void FragmentTestCacheGLES::BindTestTexture(int slot) { if (!g_Config.bFragmentTestCache) { return; diff --git a/GPU/GLES/FragmentTestCacheGLES.h b/GPU/GLES/FragmentTestCacheGLES.h index 043b236fe6..da7f1c78b8 100644 --- a/GPU/GLES/FragmentTestCacheGLES.h +++ b/GPU/GLES/FragmentTestCacheGLES.h @@ -67,6 +67,10 @@ public: void BindTestTexture(int slot); + void DeviceLost() { + Clear(false); + } + void DeviceRestore(Draw::DrawContext *draw); void Clear(bool deleteThem = true); void Decimate(); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 0b3e14489d..1dd7611211 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -58,7 +58,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) GLRenderManager *render = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - shaderManagerGL_ = new ShaderManagerGLES(render); + shaderManagerGL_ = new ShaderManagerGLES(draw); framebufferManagerGL_ = new FramebufferManagerGLES(draw, render); framebufferManager_ = framebufferManagerGL_; textureCacheGL_ = new TextureCacheGLES(draw); @@ -331,11 +331,10 @@ void GPU_GLES::DeviceLost() { // Simply drop all caches and textures. // FBOs appear to survive? Or no? // TransformDraw has registered as a GfxResourceHolder. - drawEngine_.ClearInputLayoutMap(); - shaderManagerGL_->ClearCache(false); + shaderManagerGL_->DeviceLost(); textureCacheGL_->DeviceLost(); - fragmentTestCache_.Clear(false); - depalShaderCache_.Clear(); + fragmentTestCache_.DeviceLost(); + depalShaderCache_.DeviceLost(); drawEngine_.DeviceLost(); framebufferManagerGL_->DeviceLost(); // Don't even try to access the lost device. @@ -349,9 +348,12 @@ void GPU_GLES::DeviceRestore() { UpdateCmdInfo(); UpdateVsyncInterval(true); + shaderManagerGL_->DeviceRestore(draw_); textureCacheGL_->DeviceRestore(draw_); framebufferManagerGL_->DeviceRestore(draw_); drawEngine_.DeviceRestore(draw_); + fragmentTestCache_.DeviceRestore(draw_); + depalShaderCache_.DeviceRestore(draw_); } void GPU_GLES::Reinitialize() { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 63bf981bcc..e9ed8a58d6 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -574,8 +574,9 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { } } -ShaderManagerGLES::ShaderManagerGLES(GLRenderManager *render) - : render_(render), lastShader_(nullptr), shaderSwitchDirtyUniforms_(0), diskCacheDirty_(false), fsCache_(16), vsCache_(16) { +ShaderManagerGLES::ShaderManagerGLES(Draw::DrawContext *draw) + : lastShader_(nullptr), shaderSwitchDirtyUniforms_(0), diskCacheDirty_(false), fsCache_(16), vsCache_(16) { + render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); codeBuffer_ = new char[16384]; lastFSID_.set_invalid(); lastVSID_.set_invalid(); @@ -607,6 +608,14 @@ void ShaderManagerGLES::ClearCache(bool deleteThem) { Clear(); } +void ShaderManagerGLES::DeviceLost() { + Clear(); +} + +void ShaderManagerGLES::DeviceRestore(Draw::DrawContext *draw) { + render_ = (GLRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); +} + void ShaderManagerGLES::DirtyShader() { // Forget the last shader ID lastFSID_.set_invalid(); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index c9ca56f15a..33d187301b 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -152,7 +152,7 @@ private: class ShaderManagerGLES : public ShaderManagerCommon { public: - ShaderManagerGLES(GLRenderManager *render); + ShaderManagerGLES(Draw::DrawContext *draw); ~ShaderManagerGLES(); void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected @@ -162,6 +162,9 @@ public: Shader *ApplyVertexShader(int prim, u32 vertType, VShaderID *VSID); LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, u32 vertType, int prim); + void DeviceLost(); + void DeviceRestore(Draw::DrawContext *draw); + void DirtyShader(); void DirtyLastShader() override; // disables vertex arrays From b1df7b2cd0dd29f07d75792fd59deb87caf07899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 29 May 2018 23:30:41 +0200 Subject: [PATCH 062/301] Thin3D: Handle binding a null texture better. --- ext/native/thin3d/thin3d.h | 5 +++-- ext/native/thin3d/thin3d_vulkan.cpp | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 2aa081c273..bc5927392c 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -2,7 +2,7 @@ // Does not involve context creation etc, that should be handled separately - only does drawing. // The goals may change in the future though. -// MIT licensed, by Henrik Rydgård 2014. +// MIT licensed, by Henrik RydgÃ¥rd 2014. #pragma once @@ -597,7 +597,8 @@ public: virtual void UpdateDynamicUniformBuffer(const void *ub, size_t size) = 0; void BindTexture(int stage, Texture *texture) { - BindTextures(stage, 1, &texture); + Texture *textures[1] = { texture }; + BindTextures(stage, 1, textures); } // from sampler 0 and upwards // Call this with 0 to signal that you have been drawing on your own, and need the state reset on the next pipeline bind. diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index ab9b828def..d2fa0e1619 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -730,7 +730,6 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) queue_ = vulkan->GetGraphicsQueue(); queueFamilyIndex_ = vulkan->GetGraphicsQueueFamilyIndex(); - memset(boundTextures_, 0, sizeof(boundTextures_)); VkDescriptorPoolSize dpTypes[2]; dpTypes[0].descriptorCount = 200; @@ -873,8 +872,8 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { bufferDesc.range = curPipeline_->GetUBOSize(); VkDescriptorImageInfo imageDesc; - imageDesc.imageView = boundTextures_[0]->GetImageView(); - imageDesc.sampler = boundSamplers_[0]->GetSampler(); + imageDesc.imageView = boundTextures_[0] ? boundTextures_[0]->GetImageView() : VK_NULL_HANDLE; + imageDesc.sampler = boundSamplers_[0] ? boundSamplers_[0]->GetSampler() : VK_NULL_HANDLE; #ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR imageDesc.imageLayout = VK_IMAGE_LAYOUT_GENERAL; #else @@ -1122,7 +1121,7 @@ void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, void VKContext::BindTextures(int start, int count, Texture **textures) { for (int i = start; i < start + count; i++) { boundTextures_[i] = static_cast(textures[i]); - boundImageView_[i] = boundTextures_[i]->GetImageView(); + boundImageView_[i] = boundTextures_[i] ? boundTextures_[i]->GetImageView() : VK_NULL_HANDLE; } } From 4f59ebad057aee52dad8ea4b786c78444bc14ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 29 May 2018 23:42:54 +0200 Subject: [PATCH 063/301] sprintf->snprintf. Don't think any of these are likely overflows though. --- UI/GameScreen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 740ef7467d..cf0f8c0ba5 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -216,12 +216,12 @@ void GameScreen::update() { if (info->gameSize) { char temp[256]; - sprintf(temp, "%s: %1.1f %s", ga->T("Game"), (float) (info->gameSize) / 1024.f / 1024.f, ga->T("MB")); + snprintf(temp, sizeof(temp), "%s: %1.1f %s", ga->T("Game"), (float) (info->gameSize) / 1024.f / 1024.f, ga->T("MB")); tvGameSize_->SetText(temp); - sprintf(temp, "%s: %1.2f %s", ga->T("SaveData"), (float) (info->saveDataSize) / 1024.f / 1024.f, ga->T("MB")); + snprintf(temp, sizeof(temp), "%s: %1.2f %s", ga->T("SaveData"), (float) (info->saveDataSize) / 1024.f / 1024.f, ga->T("MB")); tvSaveDataSize_->SetText(temp); if (info->installDataSize > 0) { - sprintf(temp, "%s: %1.2f %s", ga->T("InstallData"), (float) (info->installDataSize) / 1024.f / 1024.f, ga->T("MB")); + snprintf(temp, sizeof(temp), "%s: %1.2f %s", ga->T("InstallData"), (float) (info->installDataSize) / 1024.f / 1024.f, ga->T("MB")); tvInstallDataSize_->SetText(temp); tvInstallDataSize_->SetVisibility(UI::V_VISIBLE); } From 7e756544ee2c6b04bb5df3fab5845fafd612ab4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 May 2018 18:01:49 +0200 Subject: [PATCH 064/301] Add a missing virtual destructor, fix a warning --- Core/Loaders.h | 1 + ext/native/thin3d/thin3d.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/Loaders.h b/Core/Loaders.h index 05837433f8..f0e83adab4 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -110,6 +110,7 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader); class FileLoaderFactory { public: + virtual ~FileLoaderFactory() {} virtual FileLoader *ConstructFileLoader(const std::string &filename) = 0; }; void RegisterFileLoaderFactory(std::string name, std::unique_ptr factory); diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 6f4eafb3bf..965c88dc0b 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -409,7 +409,7 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u case Draw::DataFormat::R8G8B8A8_UNORM: case Draw::DataFormat::UNDEFINED: default: - WARN_LOG_REPORT_ONCE(convFromRGBA, G3D, "Unable to convert from format: %d", format); + WARN_LOG_REPORT_ONCE(convFromRGBA, G3D, "Unable to convert from format: %d", (int)format); break; } } From 3f7474e5b8fb4ce9bacbbfe5520b3b264dcfab3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 May 2018 18:14:13 +0200 Subject: [PATCH 065/301] More (disabled by default) gl error logging.. --- ext/native/thin3d/GLQueueRunner.cpp | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 653125d3fe..0c9cf6bd65 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -24,6 +24,7 @@ extern void bindDefaultFBO(); GLuint g_defaultFBO = 0; void GLQueueRunner::CreateDeviceObjects() { + CHECK_GL_ERROR_IF_DEBUG(); if (gl_extensions.EXT_texture_filter_anisotropic) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); } else { @@ -52,9 +53,11 @@ void GLQueueRunner::CreateDeviceObjects() { if (!gl_extensions.IsCoreContext) { // Not OK to query this in core profile! populate(GL_EXTENSIONS); } + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::DestroyDeviceObjects() { + CHECK_GL_ERROR_IF_DEBUG(); if (!nameCache_.empty()) { glDeleteTextures((GLsizei)nameCache_.size(), &nameCache_[0]); nameCache_.clear(); @@ -66,6 +69,7 @@ void GLQueueRunner::DestroyDeviceObjects() { readbackBufferSize_ = 0; delete[] tempBuffer_; tempBufferSize_ = 0; + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::RunInitSteps(const std::vector &steps) { @@ -83,6 +87,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glGenTextures(1, &tex->texture); glBindTexture(tex->target, tex->texture); boundTexture = tex->texture; + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::CREATE_BUFFER: @@ -91,6 +96,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glGenBuffers(1, &buffer->buffer); glBindBuffer(buffer->target_, buffer->buffer); glBufferData(buffer->target_, step.create_buffer.size, nullptr, step.create_buffer.usage); + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::BUFFER_SUBDATA: @@ -100,6 +106,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glBufferSubData(GL_ARRAY_BUFFER, step.buffer_subdata.offset, step.buffer_subdata.size, step.buffer_subdata.data); if (step.buffer_subdata.deleteData) delete[] step.buffer_subdata.data; + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::CREATE_PROGRAM: @@ -432,6 +439,7 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { } void GLQueueRunner::RunSteps(const std::vector &steps) { + CHECK_GL_ERROR_IF_DEBUG(); for (size_t i = 0; i < steps.size(); i++) { const GLRStep &step = *steps[i]; switch (step.stepType) { @@ -456,6 +464,7 @@ void GLQueueRunner::RunSteps(const std::vector &steps) { } delete steps[i]; } + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::LogSteps(const std::vector &steps) { @@ -464,6 +473,7 @@ void GLQueueRunner::LogSteps(const std::vector &steps) { void GLQueueRunner::PerformBlit(const GLRStep &step) { + CHECK_GL_ERROR_IF_DEBUG(); // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing // in ES 2.0 that actually separate them anyway of course, so doesn't matter. fbo_bind_fb_target(false, step.blit.dst->handle); @@ -492,6 +502,7 @@ void GLQueueRunner::PerformBlit(const GLRStep &step) { } void GLQueueRunner::PerformRenderPass(const GLRStep &step) { + CHECK_GL_ERROR_IF_DEBUG(); // Don't execute empty renderpasses. if (step.commands.empty()) { // Nothing to do. @@ -583,6 +594,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisable(GL_STENCIL_TEST); stencilEnabled = false; } + CHECK_GL_ERROR_IF_DEBUG(); break; case GLRRenderCommand::STENCILOP: glStencilOp(c.stencilOp.sFail, c.stencilOp.zFail, c.stencilOp.pass); @@ -608,6 +620,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glColorMask(c.blend.mask & 1, (c.blend.mask >> 1) & 1, (c.blend.mask >> 2) & 1, (c.blend.mask >> 3) & 1); colorMask = c.blend.mask; } + CHECK_GL_ERROR_IF_DEBUG(); break; case GLRRenderCommand::LOGICOP: #ifndef USING_GLES2 @@ -624,6 +637,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { logicEnabled = false; } #endif + CHECK_GL_ERROR_IF_DEBUG(); break; case GLRRenderCommand::CLEAR: // Scissor test is on, and should be on after leaving this case. If we disable it, @@ -656,6 +670,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { if (c.clear.scissorW == 0) { glEnable(GL_SCISSOR_TEST); } + CHECK_GL_ERROR_IF_DEBUG(); break; case GLRRenderCommand::INVALIDATE: { @@ -668,6 +683,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT) attachments[count++] = GL_STENCIL_BUFFER_BIT; glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments); + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::BLENDCOLOR: @@ -686,6 +702,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { #else glDepthRangef(c.viewport.vp.minZ, c.viewport.vp.maxZ); #endif + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::SCISSOR: @@ -694,6 +711,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { if (!curFB_) y = curFBHeight_ - y - c.scissor.rc.h; glScissor(c.scissor.rc.x, y, c.scissor.rc.w, c.scissor.rc.h); + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::UNIFORM4F: @@ -718,6 +736,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; } } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::UNIFORM4I: @@ -742,6 +761,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; } } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::UNIFORMMATRIX: @@ -753,6 +773,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { if (loc >= 0) { glUniformMatrix4fv(loc, 1, false, c.uniformMatrix4.m); } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::BINDTEXTURE: @@ -798,6 +819,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glUseProgram(c.program.program->program); curProgram = c.program.program; } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::BIND_VERTEX_BUFFER: @@ -861,6 +883,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } else { glDrawElementsInstanced(c.drawIndexed.mode, c.drawIndexed.count, c.drawIndexed.indexType, c.drawIndexed.indices, c.drawIndexed.instances); } + CHECK_GL_ERROR_IF_DEBUG(); break; case GLRRenderCommand::TEXTURESAMPLER: { @@ -949,6 +972,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisable(GL_DITHER); ditherEnabled = false; } + CHECK_GL_ERROR_IF_DEBUG(); break; default: Crash(); @@ -966,6 +990,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glActiveTexture(GL_TEXTURE0); activeSlot = 0; // doesn't matter, just nice. } + CHECK_GL_ERROR_IF_DEBUG(); // Wipe out the current state. glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -986,6 +1011,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } void GLQueueRunner::PerformCopy(const GLRStep &step) { + CHECK_GL_ERROR_IF_DEBUG(); GLuint srcTex = 0; GLuint dstTex = 0; GLuint target = GL_TEXTURE_2D; @@ -1047,6 +1073,7 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { void GLQueueRunner::PerformReadback(const GLRStep &pass) { using namespace Draw; + CHECK_GL_ERROR_IF_DEBUG(); GLRFramebuffer *fb = pass.readback.src; @@ -1149,6 +1176,7 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { fbo_unbind(); // Backbuffer is now bound. } + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { @@ -1168,6 +1196,7 @@ GLuint GLQueueRunner::AllocTextureName() { } u32 name = nameCache_.back(); nameCache_.pop_back(); + CHECK_GL_ERROR_IF_DEBUG(); return name; } @@ -1178,6 +1207,8 @@ GLuint GLQueueRunner::AllocTextureName() { void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { GLRFramebuffer *fbo = step.create_framebuffer.framebuffer; + CHECK_GL_ERROR_IF_DEBUG(); + // Color texture is same everywhere glGenFramebuffersEXT(1, &fbo->handle); glGenTextures(1, &fbo->color_texture.texture); @@ -1230,6 +1261,8 @@ void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); glBindTexture(GL_TEXTURE_2D, 0); + CHECK_GL_ERROR_IF_DEBUG(); + currentDrawHandle_ = fbo->handle; currentReadHandle_ = fbo->handle; } @@ -1295,6 +1328,7 @@ void GLQueueRunner::fbo_unbind() { } GLRFramebuffer::~GLRFramebuffer() { + CHECK_GL_ERROR_IF_DEBUG(); if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { if (handle) { glBindFramebuffer(GL_FRAMEBUFFER, handle); @@ -1326,4 +1360,5 @@ GLRFramebuffer::~GLRFramebuffer() { glDeleteRenderbuffers(1, &stencil_buffer); #endif } + CHECK_GL_ERROR_IF_DEBUG(); } From c964bc4ba262517ce4c35b52a3604297ce72f999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 May 2018 19:29:26 +0200 Subject: [PATCH 066/301] Fix another of these pesky GL task switching bugs. --- GPU/GLES/DrawEngineGLES.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 72861b4023..d79ee4efd4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -105,6 +105,7 @@ void DrawEngineGLES::DeviceLost() { void DrawEngineGLES::DeviceRestore(Draw::DrawContext *draw) { draw_ = draw; + render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); InitDeviceObjects(); } From 00e5da2c35639db3a485528396bce4436ea44467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 May 2018 19:47:20 +0200 Subject: [PATCH 067/301] Update to 1.6.2 --- README.md | 4 ++-- android/AndroidManifest.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e4651a678e..357cebcd79 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ For build instructions and other development tutorials, see [the wiki](https://g For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games). -What's new in 1.6.1 -------------------- +What's new in 1.6.1, 1.6.2 +-------------------------- * Crashfixes * Fix broken graphics in flOw. diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 256df60c9b..a908a45c9f 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="16020000" + android:versionName="1.6.2.0"> Date: Wed, 30 May 2018 19:40:56 +0000 Subject: [PATCH 068/301] cityhash: disable SSE 4.2 optimization on x86 $ c++ -m32 -msse4.2 -c ext/native/ext/cityhash/city.cpp ext/native/ext/cityhash/city.cpp:569:5: error: use of undeclared identifier '_mm_crc32_u64' CHUNK(0); PERMUTE3(a, h, c); ^ ext/native/ext/cityhash/city.cpp:562:9: note: expanded from macro 'CHUNK' z = _mm_crc32_u64(z, b + g); \ ^ --- ext/native/ext/cityhash/city.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/native/ext/cityhash/city.cpp b/ext/native/ext/cityhash/city.cpp index 6b5d4726d2..0428da728c 100644 --- a/ext/native/ext/cityhash/city.cpp +++ b/ext/native/ext/cityhash/city.cpp @@ -27,9 +27,6 @@ // possible hash functions, by using SIMD instructions, or by // compromising on hash quality. -#ifdef __ANDROID__ -#undef __SSE4_2__ -#endif #include "city.h" #include @@ -521,7 +518,7 @@ uint128 CityHash128(const char *s, size_t len) { CityHash128WithSeed(s, len, uint128(k0, k1)); } -#ifdef __SSE4_2__ +#if defined(__x86_64__) && defined(__SSE4_2__) #include "citycrc.h" #include From f36908698779705f6c1c049b2300c9025c27f548 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 31 May 2018 00:04:40 -0700 Subject: [PATCH 069/301] D3D11: Fix crash on screenshot. --- ext/native/thin3d/thin3d_d3d11.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 2ff8388ca0..9a3ce86e5a 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -1363,15 +1363,16 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel if (fb) { assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM); + + // TODO: Figure out where the badness really comes from. + if (bx + bw > fb->width) { + bw -= (bx + bw) - fb->width; + } + if (by + bh > fb->height) { + bh -= (by + bh) - fb->height; + } } - // TODO: Figure out where the badness really comes from. - if (bx + bw > fb->width) { - bw -= (bx + bw) - fb->width; - } - if (by + bh > fb->height) { - bh -= (by + bh) - fb->height; - } if (bh <= 0 || bw <= 0) return true; From e01e77e025ea1704469ab36679250dd55c942322 Mon Sep 17 00:00:00 2001 From: weihuoya Date: Fri, 1 Jun 2018 00:58:44 +0800 Subject: [PATCH 070/301] fx depal int precision for opengl es --- GPU/Common/DepalettizeShaderCommon.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 5482ae025d..84723b6e24 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -49,6 +49,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang if (gl_extensions.IsGLES) { WRITE(p, "#version 300 es\n"); WRITE(p, "precision mediump float;\n"); + WRITE(p, "precision highp int;\n"); } else { WRITE(p, "#version 330\n"); } From aadced94f552bfadbf6b1fad3a006f2e99799cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 1 Jun 2018 17:31:29 +0200 Subject: [PATCH 071/301] Update lang submodule --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index c2c4ad9c38..97df2d45d3 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit c2c4ad9c38c5f5e97ff022a703c470fcd53da249 +Subproject commit 97df2d45d35c5a57a615727148f7221c1fefe87f From 9485b049140f5f48cbfcd8037b5a4c8f917c60ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 1 Jun 2018 18:51:37 +0200 Subject: [PATCH 072/301] Never bind a NULL image view in Vulkan no matter what. --- GPU/Vulkan/DrawEngineVulkan.cpp | 32 ++------------------------- GPU/Vulkan/DrawEngineVulkan.h | 3 +-- ext/native/thin3d/thin3d.h | 1 + ext/native/thin3d/thin3d_vulkan.cpp | 34 ++++++++++++++++++++++++++++- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index f77a813837..b8c62f596e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -215,11 +215,6 @@ void DrawEngineVulkan::DestroyDeviceObjects() { vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); if (descriptorSetLayout_ != VK_NULL_HANDLE) vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); - if (nullTexture_) { - nullTexture_->Destroy(); - delete nullTexture_; - nullTexture_ = nullptr; - } if (vertexCache_) { vertexCache_->Destroy(vulkan_); delete vertexCache_; @@ -265,29 +260,6 @@ void DrawEngineVulkan::BeginFrame() { // TODO: How can we make this nicer... ((TessellationDataTransferVulkan *)tessDataTransfer)->SetPushBuffer(frame->pushUBO); - if (!nullTexture_) { - VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - nullTexture_ = new VulkanTexture(vulkan_, textureCache_->GetAllocator()); - nullTexture_->SetTag("Null"); - int w = 8; - int h = 8; - nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); - uint32_t bindOffset; - VkBuffer bindBuf; - uint32_t *data = (uint32_t *)frame->pushUBO->Push(w * h * 4, &bindOffset, &bindBuf); - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - // data[y*w + x] = ((x ^ y) & 1) ? 0xFF808080 : 0xFF000000; // gray/black checkerboard - data[y*w + x] = 0; // black - } - } - nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w); - nullTexture_->EndCreate(cmdInit); - } else { - nullTexture_->Touch(); - } - DirtyAllUBOs(); // Wipe the vertex cache if it's grown too large. @@ -796,7 +768,7 @@ void DrawEngineVulkan::DoFlush() { textureCache_->ApplyTexture(); textureCache_->GetVulkanHandles(imageView, sampler); if (imageView == VK_NULL_HANDLE) - imageView = nullTexture_->GetImageView(); + imageView = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::NULL_IMAGEVIEW); if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } @@ -902,7 +874,7 @@ void DrawEngineVulkan::DoFlush() { textureCache_->ApplyTexture(); textureCache_->GetVulkanHandles(imageView, sampler); if (imageView == VK_NULL_HANDLE) - imageView = nullTexture_->GetImageView(); + imageView = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::NULL_IMAGEVIEW); if (sampler == VK_NULL_HANDLE) sampler = nullSampler_; } diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 529b3b8071..2459a7cb5a 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -267,8 +267,7 @@ private: VkImageView imageView = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE; - // Null texture - VulkanTexture *nullTexture_ = nullptr; + // For null texture VkSampler nullSampler_ = VK_NULL_HANDLE; DrawEngineVulkanStats stats_; diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index bc5927392c..323850fe43 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -258,6 +258,7 @@ enum class NativeObject { BOUND_TEXTURE0_IMAGEVIEW, BOUND_TEXTURE1_IMAGEVIEW, RENDER_MANAGER, + NULL_IMAGEVIEW, }; enum FBColorDepth { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index d2fa0e1619..0f2d042aeb 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -481,6 +481,8 @@ public: return (uintptr_t)boundImageView_[1]; case NativeObject::RENDER_MANAGER: return (uintptr_t)&renderManager_; + case NativeObject::NULL_IMAGEVIEW: + return (uintptr_t)GetNullTexture(); default: Crash(); return 0; @@ -490,12 +492,15 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; private: + VulkanTexture *GetNullTexture(); VulkanContext *vulkan_ = nullptr; VulkanRenderManager renderManager_; VulkanDeviceAllocator *allocator_ = nullptr; + VulkanTexture *nullTexture_ = nullptr; + VKPipeline *curPipeline_ = nullptr; VKBuffer *curVBuffers_[4]{}; int curVBufferOffsets_[4]{}; @@ -609,6 +614,32 @@ static inline VkSamplerAddressMode AddressModeToVulkan(Draw::TextureAddressMode } } +VulkanTexture *VKContext::GetNullTexture() { + if (!nullTexture_) { + VkCommandBuffer cmdInit = renderManager_.GetInitCmd(); + nullTexture_ = new VulkanTexture(vulkan_, allocator_); + nullTexture_->SetTag("Null"); + int w = 8; + int h = 8; + nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + uint32_t bindOffset; + VkBuffer bindBuf; + uint32_t *data = (uint32_t *)push_->Push(w * h * 4, &bindOffset, &bindBuf); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + // data[y*w + x] = ((x ^ y) & 1) ? 0xFF808080 : 0xFF000000; // gray/black checkerboard + data[y*w + x] = 0; // black + } + } + nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w); + nullTexture_->EndCreate(cmdInit); + } else { + nullTexture_->Touch(); + } + return nullTexture_; +} + class VKSamplerState : public SamplerState { public: VKSamplerState(VulkanContext *vulkan, const SamplerStateDesc &desc) : vulkan_(vulkan) { @@ -791,6 +822,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) } VKContext::~VKContext() { + delete nullTexture_; allocator_->Destroy(); // We have to delete on queue, so this can free its queued deletions. vulkan_->Delete().QueueCallback([](void *ptr) { @@ -1121,7 +1153,7 @@ void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, void VKContext::BindTextures(int start, int count, Texture **textures) { for (int i = start; i < start + count; i++) { boundTextures_[i] = static_cast(textures[i]); - boundImageView_[i] = boundTextures_[i] ? boundTextures_[i]->GetImageView() : VK_NULL_HANDLE; + boundImageView_[i] = boundTextures_[i] ? boundTextures_[i]->GetImageView() : GetNullTexture()->GetImageView(); } } From c1d113e0e989eabd585a8caa276606178b4ac169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 1 Jun 2018 21:16:07 +0200 Subject: [PATCH 073/301] When creating temp framebuffers for download, size them using bufferWidth/Height instead of width/height. Or maybe we should make sure to only download within the width/height by adding more clamps at the beginning of ReadFramebufferToMemory but seems more dangerous. Plus some minor things. Should help #11113 --- Core/HLE/HLE.cpp | 2 +- GPU/D3D11/FramebufferManagerD3D11.cpp | 2 +- GPU/Directx9/FramebufferDX9.cpp | 2 +- GPU/GLES/FramebufferManagerGLES.cpp | 2 +- GPU/Vulkan/FramebufferVulkan.cpp | 2 +- UI/NativeApp.cpp | 6 +++++- ext/native/thin3d/thin3d_gl.cpp | 12 +++++++++--- 7 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 01fdfa6c61..b0bf0a06f6 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -265,7 +265,7 @@ void hleCheckCurrentCallbacks() void hleReSchedule(const char *reason) { #ifdef _DEBUG - _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "hleReSchedule: Invalid or too long reason."); + _dbg_assert_msg_(HLE, reason != nullptr && strlen(reason) < 256, "hleReSchedule: Invalid or too long reason."); #endif hleAfterSyscall |= HLE_AFTER_RESCHED; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 17c9cec5e1..cf17bdbf1b 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -569,7 +569,7 @@ void FramebufferManagerD3D11::BindFramebufferAsColorTexture(int stage, VirtualFr bool FramebufferManagerD3D11::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); if (!(nvfb->fbo)) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index d2f189ad76..6de3f054ad 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -464,7 +464,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { bool FramebufferManagerDX9::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); if (!(nvfb->fbo)) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 851c8b698a..5c357ac90e 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -579,7 +579,7 @@ bool FramebufferManagerGLES::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) } } - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth }); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth }); if (!nvfb->fbo) { ERROR_LOG(FRAMEBUF, "Error creating GL FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index c05a667d2a..b72d35cbf9 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -459,7 +459,7 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) { nvfb->colorDepth = Draw::FBO_8888; - nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); + nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth }); if (!(nvfb->fbo)) { ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight); return false; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 1f7ee8509e..7130c7d181 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -691,6 +691,9 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc); texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc); + _assert_(colorPipeline); + _assert_(texColorPipeline); + // Release these now, reference counting should ensure that they get completely released // once we delete both pipelines. inputLayout->Release(); @@ -1159,7 +1162,8 @@ bool NativeIsRestarting() { } void NativeShutdown() { - screenManager->shutdown(); + if (screenManager) + screenManager->shutdown(); delete screenManager; screenManager = nullptr; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 18891fded6..8fdd744b83 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -858,8 +858,14 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } OpenGLPipeline *pipeline = new OpenGLPipeline(&renderManager_); for (auto iter : desc.shaders) { - iter->AddRef(); - pipeline->shaders.push_back(static_cast(iter)); + if (iter) { + iter->AddRef(); + pipeline->shaders.push_back(static_cast(iter)); + } else { + ELOG("ERROR: Tried to create graphics pipeline with a null shader module"); + delete pipeline; + return nullptr; + } } if (pipeline->LinkShaders()) { // Build the rest of the virtual pipeline object. @@ -878,7 +884,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { } else { ELOG("Failed to create pipeline - shaders failed to link"); delete pipeline; - return NULL; + return nullptr; } } From 2917f3ffbb9cdf3d28fe75f6bdbb9865ef9c7cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 1 Jun 2018 22:05:21 +0200 Subject: [PATCH 074/301] Better return the right pointer. --- ext/native/thin3d/thin3d_vulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 0f2d042aeb..c71ff4f716 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -482,7 +482,7 @@ public: case NativeObject::RENDER_MANAGER: return (uintptr_t)&renderManager_; case NativeObject::NULL_IMAGEVIEW: - return (uintptr_t)GetNullTexture(); + return (uintptr_t)GetNullTexture()->GetImageView(); default: Crash(); return 0; From 04fefa2417d4ba13579445bc3c9455dd0b6edf33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 1 Jun 2018 22:16:20 +0200 Subject: [PATCH 075/301] Vulkan image readback: Fix a couple scary cases of wrong argument order in image transitions, leading to transitioning 0 mips --- ext/native/thin3d/VulkanQueueRunner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index b661f64452..1be6960540 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1182,7 +1182,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd if (step.readback.src == nullptr) { // We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT, // and then back into it. - TransitionImageLayout2(cmd, backbufferImage_, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, + TransitionImageLayout2(cmd, backbufferImage_, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_READ_BIT); @@ -1219,7 +1219,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd if (step.readback.src == nullptr) { // We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT, // and then back into it. - TransitionImageLayout2(cmd, backbufferImage_, VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, + TransitionImageLayout2(cmd, backbufferImage_, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0); From 8d3dbee82324bce1da47ff1147f99712bb3eb961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 1 Jun 2018 22:24:51 +0200 Subject: [PATCH 076/301] Fix a GL error when taking a screenshot in non-buffered. May help #11056 --- ext/native/thin3d/GLQueueRunner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0c9cf6bd65..fa423a5780 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1079,8 +1079,8 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { fbo_bind_fb_target(true, fb ? fb->handle : 0); - // Reads from the "bound for read" framebuffer. - if (gl_extensions.GLES3 || !gl_extensions.IsGLES) + // Reads from the "bound for read" framebuffer. Note that if there's no fb, it's not valid to call this. + if (fb && (gl_extensions.GLES3 || !gl_extensions.IsGLES)) glReadBuffer(GL_COLOR_ATTACHMENT0); CHECK_GL_ERROR_IF_DEBUG(); From 18bfff5f7582078b79917ba7afa86c84c194a5e4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 1 Jun 2018 21:07:09 -0700 Subject: [PATCH 077/301] UI: Reinit UI texture during game when used. Otherwise we may try to use it when it doesn't exist. This should only happen on graphics restart. Potentially related to first crash in #11116. --- UI/EmuScreen.cpp | 23 ++++++++++++++++++++--- UI/EmuScreen.h | 2 ++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 3e1b322e0d..e68cf8b5d7 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1116,6 +1116,11 @@ void EmuScreen::preRender() { using namespace Draw; DrawContext *draw = screenManager()->getDrawContext(); draw->BeginFrame(); + // Let's be consistent for the entire frame. We skip the UI texture if we don't need it. + hasVisibleUI_ = hasVisibleUI(); + if (hasVisibleUI_) { + screenManager()->getUIContext()->BeginFrame(); + } // Here we do NOT bind the backbuffer or clear the screen, unless non-buffered. // The emuscreen is different than the others - we really want to allow the game to render to framebuffers // before we ever bind the backbuffer for rendering. On mobile GPUs, switching back and forth between render @@ -1216,9 +1221,7 @@ void EmuScreen::render() { if (invalid_) return; - const bool hasVisibleUI = !osm.IsEmpty() || saveStatePreview_->GetVisibility() != UI::V_GONE || g_Config.bShowTouchControls || loadingSpinner_->GetVisibility() == UI::V_VISIBLE; - const bool showDebugUI = g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler; - if (hasVisibleUI || showDebugUI || g_Config.iShowFPSCounter != 0) { + if (hasVisibleUI_) { renderUI(); } @@ -1240,6 +1243,20 @@ void EmuScreen::render() { */ } +bool EmuScreen::hasVisibleUI() { + // Regular but uncommon UI. + if (saveStatePreview_->GetVisibility() != UI::V_GONE || loadingSpinner_->GetVisibility() == UI::V_VISIBLE) + return true; + if (!osm.IsEmpty() || g_Config.bShowTouchControls || g_Config.iShowFPSCounter != 0) + return true; + + // Debug UI. + if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler) + return true; + + return false; +} + void EmuScreen::renderUI() { using namespace Draw; diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index dd1fd0ee5c..0dd44e614a 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -56,6 +56,7 @@ private: void bootGame(const std::string &filename); bool bootAllowStorage(const std::string &filename); void bootComplete(); + bool hasVisibleUI(); void renderUI(); void processAxis(const AxisInput &axis, int direction); @@ -79,6 +80,7 @@ private: bool invalid_; bool quit_; bool stopRender_ = false; + bool hasVisibleUI_ = true; std::string errorMessage_; // If set, pauses at the end of the frame. From d029bbacc7e7758804726a55a0c47f60a31a267d Mon Sep 17 00:00:00 2001 From: weihuoya Date: Sun, 3 Jun 2018 22:29:50 +0800 Subject: [PATCH 078/301] triangle fan --- GPU/Common/DrawEngineCommon.cpp | 7 ++++++- GPU/Common/IndexGenerator.cpp | 28 ++++++++++++++++------------ GPU/Common/IndexGenerator.h | 8 ++++---- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index cf3553770d..95d6d5ad2e 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -520,7 +520,12 @@ void DrawEngineCommon::DecodeVertsStep(u8 *dest, int &i, int &decodedVerts) { dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride, dc.verts, indexLowerBound, indexUpperBound); decodedVerts += indexUpperBound - indexLowerBound + 1; - indexGen.AddPrim(dc.prim, dc.vertexCount); + + bool clockwise = true; + if (dc.cullMode != -1 && gstate.isCullEnabled() && gstate.getCullMode() != dc.cullMode) { + clockwise = false; + } + indexGen.AddPrim(dc.prim, dc.vertexCount, clockwise); } else { // It's fairly common that games issue long sequences of PRIM calls, with differing // inds pointer but the same base vertex pointer. We'd like to reuse vertices between diff --git a/GPU/Common/IndexGenerator.cpp b/GPU/Common/IndexGenerator.cpp index f8de86c905..068a5ced71 100644 --- a/GPU/Common/IndexGenerator.cpp +++ b/GPU/Common/IndexGenerator.cpp @@ -36,14 +36,14 @@ void IndexGenerator::Setup(u16 *inds) { Reset(); } -void IndexGenerator::AddPrim(int prim, int vertexCount) { +void IndexGenerator::AddPrim(int prim, int vertexCount, bool clockwise) { switch (prim) { case GE_PRIM_POINTS: AddPoints(vertexCount); break; case GE_PRIM_LINES: AddLineList(vertexCount); break; case GE_PRIM_LINE_STRIP: AddLineStrip(vertexCount); break; case GE_PRIM_TRIANGLES: AddList(vertexCount); break; - case GE_PRIM_TRIANGLE_STRIP: AddStrip(vertexCount); break; - case GE_PRIM_TRIANGLE_FAN: AddFan(vertexCount); break; + case GE_PRIM_TRIANGLE_STRIP: AddStrip(vertexCount, clockwise); break; + case GE_PRIM_TRIANGLE_FAN: AddFan(vertexCount, clockwise); break; case GE_PRIM_RECTANGLES: AddRectangles(vertexCount); break; // Same } } @@ -77,8 +77,8 @@ void IndexGenerator::AddList(int numVerts) { seenPrims_ |= 1 << GE_PRIM_TRIANGLES; } -void IndexGenerator::AddStrip(int numVerts) { - int wind = 1; +void IndexGenerator::AddStrip(int numVerts, bool clockwise) { + int wind = clockwise ? 1 : 2; const int numTris = numVerts - 2; u16 *outInds = inds_; int ibase = index_; @@ -105,14 +105,16 @@ void IndexGenerator::AddStrip(int numVerts) { } } -void IndexGenerator::AddFan(int numVerts) { +void IndexGenerator::AddFan(int numVerts, bool clockwise) { const int numTris = numVerts - 2; u16 *outInds = inds_; const int startIndex = index_; + const int v1 = clockwise ? 1 : 2; + const int v2 = clockwise ? 2 : 1; for (int i = 0; i < numTris; i++) { *outInds++ = startIndex; - *outInds++ = startIndex + i + 1; - *outInds++ = startIndex + i + 2; + *outInds++ = startIndex + i + v1; + *outInds++ = startIndex + i + v2; } inds_ = outInds; index_ += numVerts; @@ -253,11 +255,13 @@ void IndexGenerator::TranslateStrip(int numInds, const ITypeLE *inds, int indexO } template -void IndexGenerator::TranslateFan(int numInds, const ITypeLE *inds, int indexOffset) { +void IndexGenerator::TranslateFan(int numInds, const ITypeLE *inds, int indexOffset, bool clockwise) { if (numInds <= 0) return; indexOffset = index_ - indexOffset; int numTris = numInds - 2; u16 *outInds = inds_; + const int v1 = clockwise ? 1 : 2; + const int v2 = clockwise ? 2 : 1; for (int i = 0; i < numTris; i++) { *outInds++ = indexOffset + inds[0]; *outInds++ = indexOffset + inds[i + 1]; @@ -293,7 +297,7 @@ void IndexGenerator::TranslatePrim(int prim, int numInds, const u8 *inds, int in case GE_PRIM_LINE_STRIP: TranslateLineStrip(numInds, inds, indexOffset); break; case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset); break; case GE_PRIM_TRIANGLE_STRIP: TranslateStrip(numInds, inds, indexOffset, clockwise); break; - case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset); break; + case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_RECTANGLES: TranslateRectangles(numInds, inds, indexOffset); break; // Same } } @@ -305,7 +309,7 @@ void IndexGenerator::TranslatePrim(int prim, int numInds, const u16_le *inds, in case GE_PRIM_LINE_STRIP: TranslateLineStrip(numInds, inds, indexOffset); break; case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset); break; case GE_PRIM_TRIANGLE_STRIP: TranslateStrip(numInds, inds, indexOffset, clockwise); break; - case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset); break; + case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_RECTANGLES: TranslateRectangles(numInds, inds, indexOffset); break; // Same } } @@ -317,7 +321,7 @@ void IndexGenerator::TranslatePrim(int prim, int numInds, const u32_le *inds, in case GE_PRIM_LINE_STRIP: TranslateLineStrip(numInds, inds, indexOffset); break; case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset); break; case GE_PRIM_TRIANGLE_STRIP: TranslateStrip(numInds, inds, indexOffset, clockwise); break; - case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset); break; + case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_RECTANGLES: TranslateRectangles(numInds, inds, indexOffset); break; // Same } } diff --git a/GPU/Common/IndexGenerator.h b/GPU/Common/IndexGenerator.h index ab33ff4f51..877808700c 100644 --- a/GPU/Common/IndexGenerator.h +++ b/GPU/Common/IndexGenerator.h @@ -49,7 +49,7 @@ public: GEPrimitiveType Prim() const { return prim_; } - void AddPrim(int prim, int vertexCount); + void AddPrim(int prim, int vertexCount, bool clockwise = true); void TranslatePrim(int prim, int numInds, const u8 *inds, int indexOffset, bool clockwise); void TranslatePrim(int prim, int numInds, const u16_le *inds, int indexOffset, bool clockwise); void TranslatePrim(int prim, int numInds, const u32_le *inds, int indexOffset, bool clockwise); @@ -76,8 +76,8 @@ private: void AddPoints(int numVerts); // Triangles void AddList(int numVerts); - void AddStrip(int numVerts); - void AddFan(int numVerts); + void AddStrip(int numVerts, bool clockwise); + void AddFan(int numVerts, bool clockwise); // Lines void AddLineList(int numVerts); void AddLineStrip(int numVerts); @@ -97,7 +97,7 @@ private: template void TranslateStrip(int numVerts, const ITypeLE *inds, int indexOffset, bool clockwise); template - void TranslateFan(int numVerts, const ITypeLE *inds, int indexOffset); + void TranslateFan(int numVerts, const ITypeLE *inds, int indexOffset, bool clockwise); template inline void TranslateRectangles(int numVerts, const ITypeLE *inds, int indexOffset); From 9f6922221d1c9773ec6b6f23d8e007f968e8a4b7 Mon Sep 17 00:00:00 2001 From: weihuoya Date: Sun, 3 Jun 2018 22:50:35 +0800 Subject: [PATCH 079/301] fx --- GPU/Common/IndexGenerator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Common/IndexGenerator.cpp b/GPU/Common/IndexGenerator.cpp index 068a5ced71..c481bfe957 100644 --- a/GPU/Common/IndexGenerator.cpp +++ b/GPU/Common/IndexGenerator.cpp @@ -264,8 +264,8 @@ void IndexGenerator::TranslateFan(int numInds, const ITypeLE *inds, int indexOff const int v2 = clockwise ? 2 : 1; for (int i = 0; i < numTris; i++) { *outInds++ = indexOffset + inds[0]; - *outInds++ = indexOffset + inds[i + 1]; - *outInds++ = indexOffset + inds[i + 2]; + *outInds++ = indexOffset + inds[i + v1]; + *outInds++ = indexOffset + inds[i + v2]; } inds_ = outInds; count_ += numTris * 3; From 4846cfe6a5a893fc8983d8d884c5da85b7d0f277 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 17:56:09 -0700 Subject: [PATCH 080/301] Android: Reformat Java files. Mostly indentation, just ran a formatter tool and did some cleanup. No code changes. --- .../ppsspp/AudioFocusChangeListener.java | 10 +- .../src/org/ppsspp/ppsspp/CameraHelper.java | 9 +- .../org/ppsspp/ppsspp/InputDeviceState.java | 56 +- .../src/org/ppsspp/ppsspp/LocationHelper.java | 12 +- android/src/org/ppsspp/ppsspp/MogaHack.java | 141 ++-- .../src/org/ppsspp/ppsspp/NativeActivity.java | 629 +++++++++--------- android/src/org/ppsspp/ppsspp/NativeApp.java | 16 +- .../ppsspp/ppsspp/NativeEGLConfigChooser.java | 34 +- .../src/org/ppsspp/ppsspp/NativeGLView.java | 8 +- .../src/org/ppsspp/ppsspp/NativeRenderer.java | 16 +- .../org/ppsspp/ppsspp/NativeSurfaceView.java | 11 +- .../ppsspp/ppsspp/PowerSaveModeReceiver.java | 2 +- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 3 - .../org/ppsspp/ppsspp/ShortcutActivity.java | 18 +- .../org/ppsspp/ppsspp/SimpleFileChooser.java | 21 +- .../src/org/ppsspp/ppsspp/TextRenderer.java | 23 +- 16 files changed, 473 insertions(+), 536 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/AudioFocusChangeListener.java b/android/src/org/ppsspp/ppsspp/AudioFocusChangeListener.java index 960e8f40a9..02a74576b5 100644 --- a/android/src/org/ppsspp/ppsspp/AudioFocusChangeListener.java +++ b/android/src/org/ppsspp/ppsspp/AudioFocusChangeListener.java @@ -1,20 +1,20 @@ package org.ppsspp.ppsspp; + import android.media.AudioManager; import android.media.AudioManager.OnAudioFocusChangeListener; -public class AudioFocusChangeListener implements OnAudioFocusChangeListener{ +public class AudioFocusChangeListener implements OnAudioFocusChangeListener { // not used right now, but we may need to use it sometime. So just store it // for now. private boolean hasAudioFocus = false; - - + @Override public void onAudioFocusChange(int focusChange) { - switch (focusChange){ + switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: hasAudioFocus = true; break; - + case AudioManager.AUDIOFOCUS_LOSS: hasAudioFocus = false; break; diff --git a/android/src/org/ppsspp/ppsspp/CameraHelper.java b/android/src/org/ppsspp/ppsspp/CameraHelper.java index 3675f65e81..ca4bd8894a 100644 --- a/android/src/org/ppsspp/ppsspp/CameraHelper.java +++ b/android/src/org/ppsspp/ppsspp/CameraHelper.java @@ -8,7 +8,6 @@ import android.graphics.SurfaceTexture; import android.graphics.YuvImage; import android.hardware.Camera; import android.util.Log; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; @@ -42,10 +41,10 @@ class CameraHelper { // crop to expected size and convert to Jpeg Rect rect = new Rect( - (previewWidth-targetW)/2, - (previewHeight-targetH)/2, - previewWidth-(previewWidth-targetW)/2, - previewHeight-(previewHeight-targetH)/2 + (previewWidth - targetW) / 2, + (previewHeight - targetH) / 2, + previewWidth - (previewWidth - targetW) / 2, + previewHeight - (previewHeight - targetH) / 2 ); yuvImage.compressToJpeg(rect, 80, baos); NativeApp.pushCameraImage(baos.toByteArray()); diff --git a/android/src/org/ppsspp/ppsspp/InputDeviceState.java b/android/src/org/ppsspp/ppsspp/InputDeviceState.java index 09381dbd71..89e9d0f016 100644 --- a/android/src/org/ppsspp/ppsspp/InputDeviceState.java +++ b/android/src/org/ppsspp/ppsspp/InputDeviceState.java @@ -11,42 +11,44 @@ import android.view.MotionEvent; @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) public class InputDeviceState { private static final String TAG = "InputDeviceState"; - + private static final int deviceId = NativeApp.DEVICE_ID_PAD_0; private InputDevice mDevice; private int[] mAxes; - InputDevice getDevice() { return mDevice; } + InputDevice getDevice() { + return mDevice; + } @TargetApi(19) void logAdvanced(InputDevice device) { - Log.i(TAG, "Vendor ID:" + device.getVendorId() + " productId: " + device.getProductId()); + Log.i(TAG, "Vendor ID:" + device.getVendorId() + " productId: " + device.getProductId()); } - + public InputDeviceState(InputDevice device) { - mDevice = device; - int numAxes = 0; - for (MotionRange range : device.getMotionRanges()) { - if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { - numAxes += 1; - } - } + mDevice = device; + int numAxes = 0; + for (MotionRange range : device.getMotionRanges()) { + if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + numAxes += 1; + } + } - mAxes = new int[numAxes]; + mAxes = new int[numAxes]; - int i = 0; - for (MotionRange range : device.getMotionRanges()) { - if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { - mAxes[i++] = range.getAxis(); - } - } - - Log.i(TAG, "Registering input device with " + numAxes + " axes: " + device.getName()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - logAdvanced(device); - } - NativeApp.sendMessage("inputDeviceConnected", device.getName()); + int i = 0; + for (MotionRange range : device.getMotionRanges()) { + if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + mAxes[i++] = range.getAxis(); + } + } + + Log.i(TAG, "Registering input device with " + numAxes + " axes: " + device.getName()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + logAdvanced(device); + } + NativeApp.sendMessage("inputDeviceConnected", device.getName()); } public boolean onKeyDown(KeyEvent event) { @@ -54,12 +56,12 @@ public class InputDeviceState { boolean repeat = event.getRepeatCount() > 0; return NativeApp.keyDown(deviceId, keyCode, repeat); } - + public boolean onKeyUp(KeyEvent event) { int keyCode = event.getKeyCode(); - return NativeApp.keyUp(deviceId, keyCode); + return NativeApp.keyUp(deviceId, keyCode); } - + public boolean onJoystickMotion(MotionEvent event) { if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) { return false; diff --git a/android/src/org/ppsspp/ppsspp/LocationHelper.java b/android/src/org/ppsspp/ppsspp/LocationHelper.java index c6f4797d61..a71bcaeb19 100644 --- a/android/src/org/ppsspp/ppsspp/LocationHelper.java +++ b/android/src/org/ppsspp/ppsspp/LocationHelper.java @@ -48,12 +48,12 @@ class LocationHelper implements LocationListener { @Override public void onLocationChanged(Location location) { - float latitude = (float)location.getLatitude(); - float longitude = (float)location.getLongitude(); - float altitude = (float)location.getAltitude(); - float speed = location.getSpeed(); - float bearing = location.getBearing(); - long time = location.getTime() / 1000; // ms to s !! + float latitude = (float) location.getLatitude(); + float longitude = (float) location.getLongitude(); + float altitude = (float) location.getAltitude(); + float speed = location.getSpeed(); + float bearing = location.getBearing(); + long time = location.getTime() / 1000; // ms to s !! NativeApp.pushNewGpsData(latitude, longitude, altitude, speed, bearing, time); } diff --git a/android/src/org/ppsspp/ppsspp/MogaHack.java b/android/src/org/ppsspp/ppsspp/MogaHack.java index 7322fd4fd6..745a1fa8c8 100644 --- a/android/src/org/ppsspp/ppsspp/MogaHack.java +++ b/android/src/org/ppsspp/ppsspp/MogaHack.java @@ -1,27 +1,25 @@ /** * Mupen64PlusAE, an N64 emulator for the Android platform - * + * * Copyright (C) 2013 Paul Lamb - * + * * This file is part of Mupen64PlusAE. - * + * * Mupen64PlusAE is free software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * Mupen64PlusAE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License along with Mupen64PlusAE. If * not, see . - * + * * Authors: Paul Lamb */ package org.ppsspp.ppsspp; -import java.util.List; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -29,90 +27,73 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Build; import android.util.Log; - import com.bda.controller.Controller; import com.bda.controller.IControllerService; +import java.util.List; /** * Temporary hack for crash in MOGA library on Lollipop. This hack can be removed once MOGA fixes * their library. The actual issue is caused by the use of implicit service intents, which are * illegal in Lollipop, as seen in the logcat message below. - * + * *
  * {@code Service Intent must be explicit: Intent { act=com.bda.controller.IControllerService } }
  * 
- * + * * @see MOGA developer site * @see * Discussion on explicit intents */ -public class MogaHack -{ - public static void init(Controller controller, Context context ) - { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) - { - boolean mIsBound = false; - java.lang.reflect.Field fIsBound = null; - android.content.ServiceConnection mServiceConnection = null; - java.lang.reflect.Field fServiceConnection = null; - try - { - Class cMogaController = controller.getClass(); - fIsBound = cMogaController.getDeclaredField( "mIsBound" ); - fIsBound.setAccessible( true ); - mIsBound = fIsBound.getBoolean( controller ); - fServiceConnection = cMogaController.getDeclaredField( "mServiceConnection" ); - fServiceConnection.setAccessible( true ); - mServiceConnection = ( android.content.ServiceConnection ) fServiceConnection.get( controller ); - } - catch( NoSuchFieldException e ) - { - Log.e( "MogaHack", "MOGA Lollipop Hack NoSuchFieldException (get)", e ); - } - catch( IllegalAccessException e ) - { - Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (get)", e ); - } - catch( IllegalArgumentException e ) - { - Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (get)", e ); - } - if( ( !mIsBound ) && ( mServiceConnection != null ) ) - { - // Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757 - Intent intent = new Intent( IControllerService.class.getName() ); - List resolveInfos = context.getPackageManager().queryIntentServices( intent, 0 ); - if( resolveInfos == null || resolveInfos.size() != 1 ) - { - // What? this doesn't do anything. - // Log.e( "MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security." ); - } - ServiceInfo serviceInfo = resolveInfos.get( 0 ).serviceInfo; - String packageName = serviceInfo.packageName; - String className = serviceInfo.name; - intent.setComponent( new ComponentName( packageName, className ) ); - - // Start the service explicitly - context.startService( intent ); - context.bindService( intent, mServiceConnection, 1 ); - try - { - fIsBound.setBoolean( controller, true ); - } - catch( IllegalAccessException e ) - { - Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (set)", e ); - } - catch( IllegalArgumentException e ) - { - Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (set)", e ); - } - } - } - else - { - controller.init(); - } - } +public class MogaHack { + public static void init(Controller controller, Context context) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + boolean mIsBound = false; + java.lang.reflect.Field fIsBound = null; + android.content.ServiceConnection mServiceConnection = null; + java.lang.reflect.Field fServiceConnection = null; + + try { + Class cMogaController = controller.getClass(); + fIsBound = cMogaController.getDeclaredField("mIsBound"); + fIsBound.setAccessible(true); + mIsBound = fIsBound.getBoolean(controller); + fServiceConnection = cMogaController.getDeclaredField("mServiceConnection"); + fServiceConnection.setAccessible(true); + mServiceConnection = (android.content.ServiceConnection) fServiceConnection.get(controller); + } catch (NoSuchFieldException e) { + Log.e("MogaHack", "MOGA Lollipop Hack NoSuchFieldException (get)", e); + } catch (IllegalAccessException e) { + Log.e("MogaHack", "MOGA Lollipop Hack IllegalAccessException (get)", e); + } catch (IllegalArgumentException e) { + Log.e("MogaHack", "MOGA Lollipop Hack IllegalArgumentException (get)", e); + } + + if ((!mIsBound) && (mServiceConnection != null)) { + // Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757 + Intent intent = new Intent(IControllerService.class.getName()); + List resolveInfos = context.getPackageManager().queryIntentServices(intent, 0); + if (resolveInfos == null || resolveInfos.size() != 1) { + // What? this doesn't do anything. + // Log.e("MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security."); + } + ServiceInfo serviceInfo = resolveInfos.get(0).serviceInfo; + String packageName = serviceInfo.packageName; + String className = serviceInfo.name; + intent.setComponent(new ComponentName(packageName, className)); + + // Start the service explicitly + context.startService(intent); + context.bindService(intent, mServiceConnection, 1); + try { + fIsBound.setBoolean(controller, true); + } catch (IllegalAccessException e) { + Log.e("MogaHack", "MOGA Lollipop Hack IllegalAccessException (set)", e); + } catch (IllegalArgumentException e) { + Log.e("MogaHack", "MOGA Lollipop Hack IllegalArgumentException (set)", e); + } + } + } else { + controller.init(); + } + } } diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 44760b8869..563954e6b5 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -48,7 +48,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.Toast; - import java.io.File; import java.lang.reflect.Field; import java.util.List; @@ -116,14 +115,14 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private int pixelHeight; private static final String[] permissionsForStorage = { - Manifest.permission.WRITE_EXTERNAL_STORAGE + Manifest.permission.WRITE_EXTERNAL_STORAGE, }; private static final String[] permissionsForLocation = { - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION, }; private static final String[] permissionsForCamera = { - Manifest.permission.CAMERA + Manifest.permission.CAMERA }; public static final int REQUEST_CODE_STORAGE_PERMISSION = 1; @@ -132,12 +131,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Functions for the app activity to override to change behaviour. - public native void registerCallbacks(); - public native void unregisterCallbacks(); + public native void registerCallbacks(); + public native void unregisterCallbacks(); - public boolean useLowProfileButtons() { - return true; - } + public boolean useLowProfileButtons() { + return true; + } NativeRenderer getRenderer() { return nativeRenderer; @@ -158,21 +157,21 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } String getApplicationLibraryDir(ApplicationInfo application) { - String libdir = null; - try { - // Starting from Android 2.3, nativeLibraryDir is available: - Field field = ApplicationInfo.class.getField("nativeLibraryDir"); - libdir = (String) field.get(application); - } catch (SecurityException e1) { - } catch (NoSuchFieldException e1) { - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } - if (libdir == null) { - // Fallback for Android < 2.3: - libdir = application.dataDir + "/lib"; - } - return libdir; + String libdir = null; + try { + // Starting from Android 2.3, nativeLibraryDir is available: + Field field = ApplicationInfo.class.getField("nativeLibraryDir"); + libdir = (String) field.get(application); + } catch (SecurityException e1) { + } catch (NoSuchFieldException e1) { + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + if (libdir == null) { + // Fallback for Android < 2.3: + libdir = application.dataDir + "/lib"; + } + return libdir; } @TargetApi(23) @@ -212,27 +211,26 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { - case REQUEST_CODE_STORAGE_PERMISSION: - if (permissionsGranted(permissions, grantResults)) { - NativeApp.sendMessage("permission_granted", "storage"); - } else { - NativeApp.sendMessage("permission_denied", "storage"); - } - break; - case REQUEST_CODE_LOCATION_PERMISSION: - if (permissionsGranted(permissions, grantResults)) { - mLocationHelper.startLocationUpdates(); - } - break; - case REQUEST_CODE_CAMERA_PERMISSION: - if (mCameraHelper != null && permissionsGranted(permissions, grantResults)) { - mCameraHelper.startCamera(); - } - break; - default: + case REQUEST_CODE_STORAGE_PERMISSION: + if (permissionsGranted(permissions, grantResults)) { + NativeApp.sendMessage("permission_granted", "storage"); + } else { + NativeApp.sendMessage("permission_denied", "storage"); + } + break; + case REQUEST_CODE_LOCATION_PERMISSION: + if (permissionsGranted(permissions, grantResults)) { + mLocationHelper.startLocationUpdates(); + } + break; + case REQUEST_CODE_CAMERA_PERMISSION: + if (mCameraHelper != null && permissionsGranted(permissions, grantResults)) { + mCameraHelper.startCamera(); + } + break; + default: } } @@ -241,65 +239,63 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } public void Initialize() { - // Initialize audio classes. Do this here since detectOptimalAudioSettings() + // Initialize audio classes. Do this here since detectOptimalAudioSettings() // needs audioManager - this.audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); + this.audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); this.audioFocusChangeListener = new AudioFocusChangeListener(); - if (Build.VERSION.SDK_INT >= 17) { - // Get the optimal buffer sz - detectOptimalAudioSettings(); - } - powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE); - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= 17) { + // Get the optimal buffer sz + detectOptimalAudioSettings(); + } + powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (Build.VERSION.SDK_INT >= 24) { if (powerManager.isSustainedPerformanceModeSupported()) { sustainedPerfSupported = true; NativeApp.sendMessage("sustained_perf_supported", "1"); } - } + } - // isLandscape is used to trigger GetAppInfo currently, we - boolean landscape = NativeApp.isLandscape(); - Log.d(TAG, "Landscape: " + landscape); + // isLandscape is used to trigger GetAppInfo currently, we + boolean landscape = NativeApp.isLandscape(); + Log.d(TAG, "Landscape: " + landscape); - // Get system information + // Get system information ApplicationInfo appInfo = null; PackageManager packMgmr = getPackageManager(); String packageName = getPackageName(); try { - appInfo = packMgmr.getApplicationInfo(packageName, 0); - } catch (NameNotFoundException e) { - e.printStackTrace(); - throw new RuntimeException("Unable to locate assets, aborting..."); - } + appInfo = packMgmr.getApplicationInfo(packageName, 0); + } catch (NameNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException("Unable to locate assets, aborting..."); + } int deviceType = NativeApp.DEVICE_TYPE_MOBILE; UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); switch (uiModeManager.getCurrentModeType()) { case Configuration.UI_MODE_TYPE_TELEVISION: deviceType = NativeApp.DEVICE_TYPE_TV; - Log.i(TAG, "Running on an Android TV Device"); + Log.i(TAG, "Running on an Android TV Device"); break; case Configuration.UI_MODE_TYPE_DESK: deviceType = NativeApp.DEVICE_TYPE_DESKTOP; - Log.i(TAG, "Running on an Android desktop computer (!)"); + Log.i(TAG, "Running on an Android desktop computer (!)"); break; // All other device types are treated the same. } - - - isXperiaPlay = IsXperiaPlay(); + isXperiaPlay = IsXperiaPlay(); String libraryDir = getApplicationLibraryDir(appInfo); - File sdcard = Environment.getExternalStorageDirectory(); + File sdcard = Environment.getExternalStorageDirectory(); - String externalStorageDir = sdcard.getAbsolutePath(); - File filesDir = this.getFilesDir(); + String externalStorageDir = sdcard.getAbsolutePath(); + File filesDir = this.getFilesDir(); String dataDir = null; - if (filesDir != null) { - // Null has been seen in Google Play stacktraces + if (filesDir != null) { + // Null has been seen in Google Play stacktraces dataDir = filesDir.getAbsolutePath(); } String apkFilePath = appInfo.sourceDir; @@ -322,27 +318,26 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateScreenRotation("Initialize"); } - // Detect OpenGL support. - // We don't currently use this detection for anything but good to have in the log. - if (!detectOpenGLES20()) { - Log.i(TAG, "OpenGL ES 2.0 NOT detected. Things will likely go badly."); - } else { - if (detectOpenGLES30()) { - Log.i(TAG, "OpenGL ES 3.0 detected."); - } - else { - Log.i(TAG, "OpenGL ES 2.0 detected."); - } - } + // Detect OpenGL support. + // We don't currently use this detection for anything but good to have in the log. + if (!detectOpenGLES20()) { + Log.i(TAG, "OpenGL ES 2.0 NOT detected. Things will likely go badly."); + } else { + if (detectOpenGLES30()) { + Log.i(TAG, "OpenGL ES 3.0 detected."); + } else { + Log.i(TAG, "OpenGL ES 2.0 detected."); + } + } - vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE); - if (Build.VERSION.SDK_INT >= 11) { - checkForVibrator(); - } + vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + if (Build.VERSION.SDK_INT >= 11) { + checkForVibrator(); + } mLocationHelper = new LocationHelper(this); - if (Build.VERSION.SDK_INT >= 11) { - // android.graphics.SurfaceTexture is not available before version 11. + if (Build.VERSION.SDK_INT >= 11) { + // android.graphics.SurfaceTexture is not available before version 11. mCameraHelper = new CameraHelper(this); } } @@ -426,11 +421,11 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Need API 11 to check for existence of a vibrator? Zany. @TargetApi(11) public void checkForVibrator() { - if (Build.VERSION.SDK_INT >= 11) { - if (!vibrator.hasVibrator()) { - vibrator = null; - } - } + if (Build.VERSION.SDK_INT >= 11) { + if (!vibrator.hasVibrator()) { + vibrator = null; + } + } } private Runnable mEmulationRunner = new Runnable() { @@ -460,7 +455,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C public void updateDisplayMeasurements() { Display display = getWindowManager().getDefaultDisplay(); - DisplayMetrics metrics = new DisplayMetrics(); if (useImmersive() && Build.VERSION.SDK_INT >= 17) { display.getRealMetrics(metrics); @@ -470,7 +464,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C densityDpi = metrics.densityDpi; refreshRate = display.getRefreshRate(); - NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int)densityDpi, refreshRate); + NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int) densityDpi, refreshRate); } @Override @@ -497,37 +491,37 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C setVolumeControlStream(AudioManager.STREAM_MUSIC); gainAudioFocus(this.audioManager, this.audioFocusChangeListener); - NativeApp.audioInit(); + NativeApp.audioInit(); - if (javaGL) { - mGLSurfaceView = new NativeGLView(this); + if (javaGL) { + mGLSurfaceView = new NativeGLView(this); nativeRenderer = new NativeRenderer(this); mGLSurfaceView.setEGLContextClientVersion(2); mGLSurfaceView.getHolder().addCallback(NativeActivity.this); - // Setup the GLSurface and ask android for the correct - // Number of bits for r, g, b, a, depth and stencil components - // The PSP only has 16-bit Z so that should be enough. - // Might want to change this for other apps (24-bit might be useful). - // Actually, we might be able to do without both stencil and depth in - // the back buffer, but that would kill non-buffered rendering. + // Setup the GLSurface and ask android for the correct + // Number of bits for r, g, b, a, depth and stencil components + // The PSP only has 16-bit Z so that should be enough. + // Might want to change this for other apps (24-bit might be useful). + // Actually, we might be able to do without both stencil and depth in + // the back buffer, but that would kill non-buffered rendering. - // It appears some gingerbread devices blow up if you use a config chooser at all ???? (Xperia Play) - //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // It appears some gingerbread devices blow up if you use a config chooser at all ???? (Xperia Play) + //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // On some (especially older devices), things blow up later (EGL_BAD_MATCH) if we don't set the format here, - // if we specify that we want destination alpha in the config chooser, which we do. - // http://grokbase.com/t/gg/android-developers/11bj40jm4w/fall-back + // On some (especially older devices), things blow up later (EGL_BAD_MATCH) if we don't set + // the format here, if we specify that we want destination alpha in the config chooser, which we do. + // http://grokbase.com/t/gg/android-developers/11bj40jm4w/fall-back - // Needed to avoid banding on Ouya? - if (Build.MANUFACTURER == "OUYA") { - mGLSurfaceView.getHolder().setFormat(PixelFormat.RGBX_8888); - mGLSurfaceView.setEGLConfigChooser(new NativeEGLConfigChooser()); - } - // Tried to mess around with config choosers here but fail completely on Xperia Play. + // Needed to avoid banding on Ouya? + if (Build.MANUFACTURER == "OUYA") { + mGLSurfaceView.getHolder().setFormat(PixelFormat.RGBX_8888); + mGLSurfaceView.setEGLConfigChooser(new NativeEGLConfigChooser()); + } + // Tried to mess around with config choosers here but fail completely on Xperia Play. mGLSurfaceView.setRenderer(nativeRenderer); setContentView(mGLSurfaceView); - } else { + } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -536,16 +530,17 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } mSurfaceView = new NativeSurfaceView(NativeActivity.this); - mSurfaceView.getHolder().addCallback(NativeActivity.this); - Log.i(TAG, "setcontentview before"); + mSurfaceView.getHolder().addCallback(NativeActivity.this); + Log.i(TAG, "setcontentview before"); setContentView(mSurfaceView); Log.i(TAG, "setcontentview after"); ensureRenderLoop(); - } - } + } + } private Point desiredSize = new Point(); private int badOrientationCount = 0; + @Override public void surfaceCreated(SurfaceHolder holder) { pixelWidth = holder.getSurfaceFrame().width(); @@ -568,7 +563,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); - NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int)densityDpi, refreshRate); + NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int) densityDpi, refreshRate); getDesiredBackbufferSize(desiredSize); // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. @@ -586,15 +581,15 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.v(TAG, "surfaceChanged: isCreating:" + holder.isCreating() + " holder: " + holder.toString()); if (holder.isCreating() && desiredSize.x > 0 && desiredSize.y > 0) { - // We have called setFixedSize which will trigger another surfaceChanged after the initial one. This one is the original one - // and we don't care about it. + // We have called setFixedSize which will trigger another surfaceChanged after the initial + // one. This one is the original one and we don't care about it. Log.w(TAG, "holder.isCreating = true, ignoring. width=" + width + " height=" + height + " desWidth=" + desiredSize.x + " desHeight=" + desiredSize.y); return; } Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); NativeApp.backbufferResize(width, height, format); mSurface = holder.getSurface(); - if (!javaGL) { + if (!javaGL) { // If we got a surface, this starts the thread. If not, it doesn't. if (mSurface == null) { joinRenderLoopThread(); @@ -619,8 +614,8 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Invariants: After this, mRenderLoopThread will be set, and the thread will be running. protected synchronized void ensureRenderLoop() { if (javaGL) { - Log.e(TAG, "JavaGL - should not get into ensureRenderLoop."); - return; + Log.e(TAG, "JavaGL - should not get into ensureRenderLoop."); + return; } if (mSurface == null) { Log.w(TAG, "ensureRenderLoop - not starting thread, needs surface"); @@ -637,8 +632,8 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Invariants: After this, mRenderLoopThread will be null, and the thread has exited. private synchronized void joinRenderLoopThread() { if (javaGL) { - Log.e(TAG, "JavaGL - should not get into joinRenderLoopThread."); - return; + Log.e(TAG, "JavaGL - should not get into joinRenderLoopThread."); + return; } if (mRenderLoopThread != null) { @@ -656,23 +651,23 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } - @TargetApi(19) + @TargetApi(19) void setupSystemUiCallback() { - getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int visibility) { - if (visibility == 0) { - updateSystemUiVisibility(); - } - } - }); - } + getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int visibility) { + if (visibility == 0) { + updateSystemUiVisibility(); + } + } + }); + } - @Override - protected void onStop() { - super.onStop(); - Log.i(TAG, "onStop - do nothing special"); - } + @Override + protected void onStop() { + super.onStop(); + Log.i(TAG, "onStop - do nothing special"); + } @Override protected void onDestroy() { @@ -709,46 +704,46 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } - @Override - protected void onPause() { + @Override + protected void onPause() { super.onPause(); Log.i(TAG, "onPause"); loseAudioFocus(this.audioManager, this.audioFocusChangeListener); NativeApp.pause(); - if (!javaGL) { + if (!javaGL) { mSurfaceView.onPause(); Log.i(TAG, "Joining render thread..."); joinRenderLoopThread(); Log.i(TAG, "Joined render thread"); - } else { + } else { if (mGLSurfaceView != null) { mGLSurfaceView.onPause(); } else { Log.e(TAG, "mGLSurfaceView really shouldn't be null in onPause"); } - } + } Log.i(TAG, "onPause completed"); } - private boolean detectOpenGLES20() { - ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - ConfigurationInfo info = am.getDeviceConfigurationInfo(); - return info.reqGlEsVersion >= 0x20000; - } + private boolean detectOpenGLES20() { + ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + ConfigurationInfo info = am.getDeviceConfigurationInfo(); + return info.reqGlEsVersion >= 0x20000; + } - private boolean detectOpenGLES30() { - ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - ConfigurationInfo info = am.getDeviceConfigurationInfo(); - return info.reqGlEsVersion >= 0x30000; - } + private boolean detectOpenGLES30() { + ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + ConfigurationInfo info = am.getDeviceConfigurationInfo(); + return info.reqGlEsVersion >= 0x30000; + } @Override protected void onResume() { super.onResume(); updateSustainedPerformanceMode(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - updateSystemUiVisibility(); - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + updateSystemUiVisibility(); + } // OK, config should be initialized, we can query for screen rotation. if (javaGL || Build.VERSION.SDK_INT >= 9) { updateScreenRotation("onResume"); @@ -776,80 +771,79 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } - @Override - public void onConfigurationChanged(Configuration newConfig) { - Log.i(TAG, "onConfigurationChanged"); - super.onConfigurationChanged(newConfig); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - updateSystemUiVisibility(); - } - densityDpi = (float)newConfig.densityDpi; - } + @Override + public void onConfigurationChanged(Configuration newConfig) { + Log.i(TAG, "onConfigurationChanged"); + super.onConfigurationChanged(newConfig); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + updateSystemUiVisibility(); + } + densityDpi = (float) newConfig.densityDpi; + } - //keep this static so we can call this even if we don't - //instantiate NativeAudioPlayer + // keep this static so we can call this even if we don't + // instantiate NativeAudioPlayer public static void gainAudioFocus(AudioManager audioManager, AudioFocusChangeListener focusChangeListener) { if (audioManager != null) { - audioManager.requestAudioFocus(focusChangeListener, - AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); + audioManager.requestAudioFocus(focusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } } - //keep this static so we can call this even if we don't - //instantiate NativeAudioPlayer - public static void loseAudioFocus(AudioManager audioManager,AudioFocusChangeListener focusChangeListener){ + // keep this static so we can call this even if we don't + // instantiate NativeAudioPlayer + public static void loseAudioFocus(AudioManager audioManager, AudioFocusChangeListener focusChangeListener) { if (audioManager != null) { audioManager.abandonAudioFocus(focusChangeListener); } } - // We simply grab the first input device to produce an event and ignore all others that are connected. - @TargetApi(Build.VERSION_CODES.GINGERBREAD) + // We simply grab the first input device to produce an event and ignore all others that are connected. + @TargetApi(Build.VERSION_CODES.GINGERBREAD) private InputDeviceState getInputDeviceState(InputEvent event) { - InputDevice device = event.getDevice(); - if (device == null) { - return null; - } - if (inputPlayerA == null) { - inputPlayerADesc = getInputDesc(device); - Log.i(TAG, "Input player A registered: desc = " + inputPlayerADesc); - inputPlayerA = new InputDeviceState(device); - } + InputDevice device = event.getDevice(); + if (device == null) { + return null; + } + if (inputPlayerA == null) { + inputPlayerADesc = getInputDesc(device); + Log.i(TAG, "Input player A registered: desc = " + inputPlayerADesc); + inputPlayerA = new InputDeviceState(device); + } - if (inputPlayerA.getDevice() == device) { - return inputPlayerA; - } + if (inputPlayerA.getDevice() == device) { + return inputPlayerA; + } - if (inputPlayerB == null) { - Log.i(TAG, "Input player B registered: desc = " + getInputDesc(device)); - inputPlayerB = new InputDeviceState(device); - } + if (inputPlayerB == null) { + Log.i(TAG, "Input player B registered: desc = " + getInputDesc(device)); + inputPlayerB = new InputDeviceState(device); + } - if (inputPlayerB.getDevice() == device) { - return inputPlayerB; - } + if (inputPlayerB.getDevice() == device) { + return inputPlayerB; + } - if (inputPlayerC == null) { - Log.i(TAG, "Input player C registered"); - inputPlayerC = new InputDeviceState(device); - } + if (inputPlayerC == null) { + Log.i(TAG, "Input player C registered"); + inputPlayerC = new InputDeviceState(device); + } - if (inputPlayerC.getDevice() == device) { - return inputPlayerC; - } + if (inputPlayerC.getDevice() == device) { + return inputPlayerC; + } - return inputPlayerA; - } + return inputPlayerA; + } - public boolean IsXperiaPlay() { - return android.os.Build.MODEL.equals("R800a") || android.os.Build.MODEL.equals("R800i") || android.os.Build.MODEL.equals("R800x") || android.os.Build.MODEL.equals("R800at") || android.os.Build.MODEL.equals("SO-01D") || android.os.Build.MODEL.equals("zeus"); - } + public boolean IsXperiaPlay() { + return android.os.Build.MODEL.equals("R800a") || android.os.Build.MODEL.equals("R800i") || android.os.Build.MODEL.equals("R800x") || android.os.Build.MODEL.equals("R800at") || android.os.Build.MODEL.equals("SO-01D") || android.os.Build.MODEL.equals("zeus"); + } - // We grab the keys before onKeyDown/... even see them. This is also better because it lets us - // distinguish devices. - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && !isXperiaPlay) { + // We grab the keys before onKeyDown/... even see them. This is also better because it lets us + // distinguish devices. + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && !isXperiaPlay) { InputDeviceState state = getInputDeviceState(event); if (state == null) { return super.dispatchKeyEvent(event); @@ -892,14 +886,14 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C break; } } - } + } - // Let's go through the old path (onKeyUp, onKeyDown). + // Let's go through the old path (onKeyUp, onKeyDown). return super.dispatchKeyEvent(event); - } + } @TargetApi(16) - static public String getInputDesc(InputDevice input) { + public static String getInputDesc(InputDevice input) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { return input.getDescriptor(); } else { @@ -916,27 +910,27 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C public boolean onGenericMotionEvent(MotionEvent event) { // Log.d(TAG, "onGenericMotionEvent: " + event); if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) { - if (Build.VERSION.SDK_INT >= 12) { - InputDeviceState state = getInputDeviceState(event); - if (state == null) { - Log.w(TAG, "Joystick event but failed to get input device state."); - return super.onGenericMotionEvent(event); - } - state.onJoystickMotion(event); - return true; - } + if (Build.VERSION.SDK_INT >= 12) { + InputDeviceState state = getInputDeviceState(event); + if (state == null) { + Log.w(TAG, "Joystick event but failed to get input device state."); + return super.onGenericMotionEvent(event); + } + state.onJoystickMotion(event); + return true; + } } if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - switch (event.getAction()) { - case MotionEvent.ACTION_HOVER_MOVE: - // process the mouse hover movement... - return true; - case MotionEvent.ACTION_SCROLL: - NativeApp.mouseWheelEvent(event.getX(), event.getY()); - return true; - } - } + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_MOVE: + // process the mouse hover movement... + return true; + case MotionEvent.ACTION_SCROLL: + NativeApp.mouseWheelEvent(event.getX(), event.getY()); + return true; + } + } return super.onGenericMotionEvent(event); } @@ -1015,19 +1009,19 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C return NativeApp.keyUp(0, keyCode); } } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) { Uri selectedImage = data.getData(); - String[] filePathColumn = { MediaStore.Images.Media.DATA }; - Cursor cursor = getContentResolver().query(selectedImage, - filePathColumn, null, null, null); + String[] filePathColumn = {MediaStore.Images.Media.DATA}; + Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null); cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String picturePath = cursor.getString(columnIndex); cursor.close(); - NativeApp.sendMessage("bgImage_updated" , picturePath); + NativeApp.sendMessage("bgImage_updated", picturePath); } } @@ -1070,17 +1064,17 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // The return value is sent elsewhere. TODO in java, in SendMessage in C++. public void inputBox(final String title, String defaultText, String defaultAction) { - final FrameLayout fl = new FrameLayout(this); - final EditText input = new EditText(this); - input.setGravity(Gravity.CENTER); + final FrameLayout fl = new FrameLayout(this); + final EditText input = new EditText(this); + input.setGravity(Gravity.CENTER); - FrameLayout.LayoutParams editBoxLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); - editBoxLayout.setMargins(2, 20, 2, 20); - fl.addView(input, editBoxLayout); + FrameLayout.LayoutParams editBoxLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); + editBoxLayout.setMargins(2, 20, 2, 20); + fl.addView(input, editBoxLayout); - input.setInputType(InputType.TYPE_CLASS_TEXT); - input.setText(defaultText); - input.selectAll(); + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setText(defaultText); + input.selectAll(); // Lovely! AlertDialog.Builder bld; @@ -1095,30 +1089,31 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C else bld = createDialogBuilderNew(); - AlertDialog dlg = bld - .setView(fl) - .setTitle(title) - .setPositiveButton(defaultAction, new DialogInterface.OnClickListener(){ - @Override - public void onClick(DialogInterface d, int which) { - NativeApp.sendMessage("inputbox_completed", title + ":" + input.getText().toString()); - d.dismiss(); - } - }) - .setNegativeButton("Cancel", new DialogInterface.OnClickListener(){ - @Override - public void onClick(DialogInterface d, int which) { - NativeApp.sendMessage("inputbox_failed", ""); - d.cancel(); - } - }).create(); + AlertDialog dlg = bld + .setView(fl) + .setTitle(title) + .setPositiveButton(defaultAction, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface d, int which) { + NativeApp.sendMessage("inputbox_completed", title + ":" + input.getText().toString()); + d.dismiss(); + } + }) + .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface d, int which) { + NativeApp.sendMessage("inputbox_failed", ""); + d.cancel(); + } + }) + .create(); - dlg.setCancelable(true); - dlg.show(); - } + dlg.setCancelable(true); + dlg.show(); + } - public boolean processCommand(String command, String params) { - SurfaceView surfView = javaGL ? mGLSurfaceView : mSurfaceView; + public boolean processCommand(String command, String params) { + SurfaceView surfView = javaGL ? mGLSurfaceView : mSurfaceView; if (command.equals("launchBrowser")) { try { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(params)); @@ -1133,24 +1128,22 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C try { Intent send = new Intent(Intent.ACTION_SENDTO); String uriText; - uriText = "mailto:email@gmail.com" + "?subject=Your app is..." - + "&body=great! Or?"; + uriText = "mailto:email@gmail.com" + "?subject=Your app is..." + "&body=great! Or?"; uriText = uriText.replace(" ", "%20"); Uri uri = Uri.parse(uriText); send.setData(uri); startActivity(Intent.createChooser(send, "E-mail the app author!")); return true; - } catch (Exception e) { // For example, android.content.ActivityNotFoundException + } catch (Exception e) { // For example, android.content.ActivityNotFoundException Log.e(TAG, e.toString()); return false; } } else if (command.equals("bgImage_browse")) { try { - Intent i = new Intent(Intent.ACTION_PICK, - android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, RESULT_LOAD_IMAGE); return true; - } catch (Exception e) { // For example, android.content.ActivityNotFoundException + } catch (Exception e) { // For example, android.content.ActivityNotFoundException Log.e(TAG, e.toString()); return false; } @@ -1161,7 +1154,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C share.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + params)); startActivity(Intent.createChooser(share, "Share Picture")); return true; - } catch (Exception e) { // For example, android.content.ActivityNotFoundException + } catch (Exception e) { // For example, android.content.ActivityNotFoundException Log.e(TAG, e.toString()); return false; } @@ -1173,7 +1166,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C sendIntent.setAction(Intent.ACTION_SEND); startActivity(sendIntent); return true; - } catch (Exception e) { // For example, android.content.ActivityNotFoundException + } catch (Exception e) { // For example, android.content.ActivityNotFoundException Log.e(TAG, e.toString()); return false; } @@ -1181,16 +1174,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C try { String twitter_user_name = params; try { - startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse("twitter://user?screen_name=" - + twitter_user_name))); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("twitter://user?screen_name=" + twitter_user_name))); } catch (Exception e) { - startActivity(new Intent( - Intent.ACTION_VIEW, - Uri.parse("https://twitter.com/#!/" + twitter_user_name))); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/#!/" + twitter_user_name))); } return true; - } catch (Exception e) { // For example, android.content.ActivityNotFoundException + } catch (Exception e) { // For example, android.content.ActivityNotFoundException Log.e(TAG, e.toString()); return false; } @@ -1208,15 +1197,11 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // No idea what the point of the ApplicationWindowToken is or if it // matters where we get it from... - inputMethodManager.toggleSoftInputFromWindow( - surfView.getApplicationWindowToken(), - InputMethodManager.SHOW_FORCED, 0); + inputMethodManager.toggleSoftInputFromWindow(surfView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0); return true; } else if (command.equals("hideKeyboard") && surfView != null) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.toggleSoftInputFromWindow( - surfView.getApplicationWindowToken(), - InputMethodManager.SHOW_FORCED, 0); + inputMethodManager.toggleSoftInputFromWindow(surfView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0); return true; } else if (command.equals("inputbox")) { String title = "Input"; @@ -1246,19 +1231,19 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // permission. if (surfView != null) { switch (milliseconds) { - case -1: - surfView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - break; - case -2: - surfView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - break; - case -3: - surfView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - break; - default: - // Requires the vibrate permission, which we don't have, so disabled. - // vibrator.vibrate(milliseconds); - break; + case -1: + surfView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + break; + case -2: + surfView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + break; + case -3: + surfView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + break; + default: + // Requires the vibrate permission, which we don't have, so disabled. + // vibrator.vibrate(milliseconds); + break; } } else { Log.e(TAG, "Can't vibrate, no surface view"); @@ -1323,21 +1308,17 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } - return false; - } + return false; + } - @SuppressLint("NewApi") + @SuppressLint("NewApi") @Override - public void recreate() - { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) - { - super.recreate(); - } - else - { - startActivity(getIntent()); - finish(); - } - } + public void recreate() { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + super.recreate(); + } else { + startActivity(getIntent()); + finish(); + } + } } diff --git a/android/src/org/ppsspp/ppsspp/NativeApp.java b/android/src/org/ppsspp/ppsspp/NativeApp.java index dd78133e0c..140f3edd59 100644 --- a/android/src/org/ppsspp/ppsspp/NativeApp.java +++ b/android/src/org/ppsspp/ppsspp/NativeApp.java @@ -1,17 +1,16 @@ package org.ppsspp.ppsspp; - // Note that the display* methods are in NativeRenderer.java public class NativeApp { - public final static int DEVICE_ID_DEFAULT = 0; - public final static int DEVICE_ID_KEYBOARD = 1; - public final static int DEVICE_ID_MOUSE = 2; - public final static int DEVICE_ID_PAD_0 = 10; + public static final int DEVICE_ID_DEFAULT = 0; + public static final int DEVICE_ID_KEYBOARD = 1; + public static final int DEVICE_ID_MOUSE = 2; + public static final int DEVICE_ID_PAD_0 = 10; - public final static int DEVICE_TYPE_MOBILE = 0; - public final static int DEVICE_TYPE_TV = 1; - public final static int DEVICE_TYPE_DESKTOP = 2; + public static final int DEVICE_TYPE_MOBILE = 0; + public static final int DEVICE_TYPE_TV = 1; + public static final int DEVICE_TYPE_DESKTOP = 2; public static native void init(String model, int deviceType, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String cacheDir, String shortcutParam, int androidVersion, String board); public static native void audioInit(); @@ -56,4 +55,3 @@ public class NativeApp { public static native void pushCameraImage(byte[] image); } - diff --git a/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java b/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java index bdbca3c27d..1ec9ce5680 100644 --- a/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java +++ b/android/src/org/ppsspp/ppsspp/NativeEGLConfigChooser.java @@ -1,11 +1,10 @@ package org.ppsspp.ppsspp; -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLDisplay; - import android.opengl.GLSurfaceView.EGLConfigChooser; import android.util.Log; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; public class NativeEGLConfigChooser implements EGLConfigChooser { private static final String TAG = "NativeEGLConfigChooser"; @@ -24,6 +23,7 @@ public class NativeEGLConfigChooser implements EGLConfigChooser { public int stencil; public int depth; public int samples; + public void Log() { Log.i(TAG, "EGLConfig: red=" + red + " green=" + green + " blue=" + blue + " alpha=" + alpha + " depth=" + depth + " stencil=" + stencil + " samples=" + samples); } @@ -79,22 +79,22 @@ public class NativeEGLConfigChooser implements EGLConfigChooser { }; int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed when counting"); - } + if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) { + throw new IllegalArgumentException("eglChooseConfig failed when counting"); + } - int numConfigs = num_config[0]; - Log.i(TAG, "There are " + numConfigs + " egl configs"); - if (numConfigs <= 0) { - throw new IllegalArgumentException("No configs match configSpec"); - } + int numConfigs = num_config[0]; + Log.i(TAG, "There are " + numConfigs + " egl configs"); + if (numConfigs <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } EGLConfig[] eglConfigs = new EGLConfig[numConfigs]; if (!egl.eglChooseConfig(display, configSpec, eglConfigs, numConfigs, num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed when retrieving"); - } + throw new IllegalArgumentException("eglChooseConfig failed when retrieving"); + } - ConfigAttribs [] configs = getConfigAttribs(egl, display, eglConfigs); + ConfigAttribs[] configs = getConfigAttribs(egl, display, eglConfigs); ConfigAttribs chosen = null; @@ -103,7 +103,6 @@ public class NativeEGLConfigChooser implements EGLConfigChooser { configs[i].Log(); } - // We now ignore destination alpha as a workaround for the Mali issue // where we get badly composited if we use it. // Though, that may be possible to fix by using EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_NONE. @@ -201,12 +200,11 @@ public class NativeEGLConfigChooser implements EGLConfigChooser { } if (chosen == null) { - throw new IllegalArgumentException("Failed to find a valid EGL config"); + throw new IllegalArgumentException("Failed to find a valid EGL config"); } Log.i(TAG, "Final chosen config: "); chosen.Log(); return chosen.config; } - } diff --git a/android/src/org/ppsspp/ppsspp/NativeGLView.java b/android/src/org/ppsspp/ppsspp/NativeGLView.java index 68c8a5082c..ce2e4be677 100644 --- a/android/src/org/ppsspp/ppsspp/NativeGLView.java +++ b/android/src/org/ppsspp/ppsspp/NativeGLView.java @@ -18,8 +18,6 @@ import android.os.Build; import android.os.Handler; import android.util.Log; import android.view.MotionEvent; -import android.view.View; - import com.bda.controller.*; public class NativeGLView extends GLSurfaceView implements SensorEventListener, ControllerListener { @@ -37,7 +35,7 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, super(activity); mActivity = activity; - mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE); + mSensorManager = (SensorManager) activity.getSystemService(Activity.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mController = Controller.getInstance(activity); @@ -89,7 +87,7 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, if (code != 0) { if (canReadToolType) { int tool = getToolType(ev, i); - code |= tool << 10; // We use the Android tool type codes + code |= tool << 10; // We use the Android tool type codes } // Can't use || due to short circuit evaluation numTouchesHandled += NativeApp.touch(ev.getX(i), ev.getY(i), code, pid) ? 1 : 0; @@ -161,7 +159,7 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, } } - boolean repeat = false; // Moga has no repeats? + boolean repeat = false; // Moga has no repeats? switch (event.getAction()) { case KeyEvent.ACTION_DOWN: NativeApp.keyDown(NativeApp.DEVICE_ID_PAD_0, event.getKeyCode(), repeat); diff --git a/android/src/org/ppsspp/ppsspp/NativeRenderer.java b/android/src/org/ppsspp/ppsspp/NativeRenderer.java index 288beba61a..2433b2b637 100644 --- a/android/src/org/ppsspp/ppsspp/NativeRenderer.java +++ b/android/src/org/ppsspp/ppsspp/NativeRenderer.java @@ -1,15 +1,10 @@ package org.ppsspp.ppsspp; +import android.opengl.GLSurfaceView; +import android.util.Log; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -import android.graphics.Point; -import android.opengl.GLES20; -import android.opengl.GLSurfaceView; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Display; - public class NativeRenderer implements GLSurfaceView.Renderer { private static String TAG = "NativeRenderer"; private NativeActivity mActivity; @@ -35,12 +30,13 @@ public class NativeRenderer implements GLSurfaceView.Renderer { // Actually, it seems that it is here we should recreate lost GL objects. displayInit(); } - + public void onSurfaceChanged(GL10 unused, int width, int height) { } // Note: This also means "device lost" and you should reload - // all buffered objects. - public native void displayInit(); + // all buffered objects. + public native void displayInit(); + public native void displayRender(); } diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 1e13dba115..d09952b6b5 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -13,12 +13,9 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Build; import android.os.Handler; -// import android.os.Build; -// import android.util.Log; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceView; - import com.bda.controller.*; public class NativeSurfaceView extends SurfaceView implements SensorEventListener, ControllerListener { @@ -35,7 +32,7 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene Log.i(TAG, "NativeSurfaceView"); - mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE); + mSensorManager = (SensorManager) activity.getSystemService(Activity.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mController = Controller.getInstance(activity); @@ -62,8 +59,6 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; int numTouchesHandled = 0; - //float scaleX = (float)mActivity.getRenderer().getDpiScaleX(); - //float scaleY = (float)mActivity.getRenderer().getDpiScaleY(); for (int i = 0; i < ev.getPointerCount(); i++) { int pid = ev.getPointerId(i); int code = 0; @@ -92,7 +87,7 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene if (code != 0) { if (canReadToolType) { int tool = getToolType(ev, i); - code |= tool << 10; // We use the Android tool type codes + code |= tool << 10; // We use the Android tool type codes } // Can't use || due to short circuit evaluation numTouchesHandled += NativeApp.touch(ev.getX(i), ev.getY(i), code, pid) ? 1 : 0; @@ -157,7 +152,7 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene } } - boolean repeat = false; // Moga has no repeats? + boolean repeat = false; // Moga has no repeats? switch (event.getAction()) { case KeyEvent.ACTION_DOWN: NativeApp.keyDown(NativeApp.DEVICE_ID_PAD_0, event.getKeyCode(), repeat); diff --git a/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java b/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java index 7620ad3322..69b396dfa8 100644 --- a/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java +++ b/android/src/org/ppsspp/ppsspp/PowerSaveModeReceiver.java @@ -55,7 +55,7 @@ public class PowerSaveModeReceiver extends BroadcastReceiver { @TargetApi(21) private static boolean getNativePowerSaving(final Context context) { - final PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); return pm.isPowerSaveMode(); } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 98141c4a22..f96705dd97 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -7,7 +7,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Looper; import android.util.Log; -import android.widget.Toast; public class PpssppActivity extends NativeActivity { private static final String TAG = "PpssppActivity"; @@ -58,7 +57,6 @@ public class PpssppActivity extends NativeActivity { } Looper.loop(); } - }.start(); try { @@ -95,7 +93,6 @@ public class PpssppActivity extends NativeActivity { super.onCreate(savedInstanceState); } - // called by the C++ code through JNI. Dispatch anything we can't directly handle // on the gfx thread to the UI thread. public void postCommand(String command, String parameter) { diff --git a/android/src/org/ppsspp/ppsspp/ShortcutActivity.java b/android/src/org/ppsspp/ppsspp/ShortcutActivity.java index dde0eeb703..8b33203d7e 100644 --- a/android/src/org/ppsspp/ppsspp/ShortcutActivity.java +++ b/android/src/org/ppsspp/ppsspp/ShortcutActivity.java @@ -1,7 +1,5 @@ package org.ppsspp.ppsspp; -import java.io.File; - import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; @@ -11,10 +9,11 @@ import android.os.Bundle; import android.os.Environment; import android.os.Looper; import android.util.Log; +import java.io.File; /** - * This class will respond to android.intent.action.CREATE_SHORTCUT intent from - * launcher homescreen. Register this class in AndroidManifest.xml. + * This class will respond to android.intent.action.CREATE_SHORTCUT intent from launcher homescreen. + * Register this class in AndroidManifest.xml. */ public class ShortcutActivity extends Activity { private static final String TAG = "PPSSPP"; @@ -24,9 +23,7 @@ public class ShortcutActivity extends Activity { super.onCreate(savedInstanceState); // Show file selector dialog here. - SimpleFileChooser fileDialog = new SimpleFileChooser(this, - Environment.getExternalStorageDirectory(), - onFileSelectedListener); + SimpleFileChooser fileDialog = new SimpleFileChooser(this, Environment.getExternalStorageDirectory(), onFileSelectedListener); fileDialog.showDialog(); } @@ -56,10 +53,8 @@ public class ShortcutActivity extends Activity { Intent responseIntent = new Intent(); responseIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); responseIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name); - ShortcutIconResource iconResource = Intent.ShortcutIconResource - .fromContext(this, R.drawable.ic_launcher); - responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, - iconResource); + ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.drawable.ic_launcher); + responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); setResult(RESULT_OK, responseIntent); @@ -97,5 +92,4 @@ public class ShortcutActivity extends Activity { respondToShortcutRequest(file.getAbsolutePath()); } }; - } diff --git a/android/src/org/ppsspp/ppsspp/SimpleFileChooser.java b/android/src/org/ppsspp/ppsspp/SimpleFileChooser.java index 9e73fbb119..bee2c4bc16 100644 --- a/android/src/org/ppsspp/ppsspp/SimpleFileChooser.java +++ b/android/src/org/ppsspp/ppsspp/SimpleFileChooser.java @@ -1,21 +1,17 @@ package org.ppsspp.ppsspp; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; -import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.os.Environment; - -/** - * Simple dialog to pick file. - */ +/** Simple dialog to pick file. */ public class SimpleFileChooser { - public interface FileSelectedListener { void onFileSelected(File file); } @@ -27,8 +23,7 @@ public class SimpleFileChooser { private String[] mFileList; private File mCurrentPath; - public SimpleFileChooser(Activity activity, File path, - FileSelectedListener listener) { + public SimpleFileChooser(Activity activity, File path, FileSelectedListener listener) { this.mActivity = activity; this.mFileListener = listener; if (!path.exists()) @@ -83,8 +78,7 @@ public class SimpleFileChooser { return 1; else // when both are folders or both are files, sort by name - return file1.getName().toUpperCase() - .compareTo(file2.getName().toUpperCase()); + return file1.getName().toUpperCase().compareTo(file2.getName().toUpperCase()); } }; @@ -107,5 +101,4 @@ public class SimpleFileChooser { } } }; - } diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index e2e4c37bef..69d411e32d 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -1,21 +1,22 @@ package org.ppsspp.ppsspp; + import android.content.Context; import android.graphics.*; import android.util.Log; -import java.nio.ByteBuffer; - public class TextRenderer { private static Paint p; private static Paint bg; private static Typeface robotoCondensed; private static final String TAG = "TextRenderer"; + static { p = new Paint(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); p.setColor(Color.WHITE); bg = new Paint(); bg.setColor(Color.BLACK); } + public static void init(Context ctx) { robotoCondensed = Typeface.createFromAsset(ctx.getAssets(), "Roboto-Condensed.ttf"); if (robotoCondensed != null) { @@ -25,23 +26,25 @@ public class TextRenderer { Log.e(TAG, "Failed to load Roboto Condensed"); } } + private static Point measureLine(String string, double textSize) { int w; if (string.length() > 0) { - p.setTextSize((float)textSize); - w = (int)p.measureText(string); - // Round width up to even already here to avoid annoyances from odd-width 16-bit textures which - // OpenGL does not like - each line must be 4-byte aligned + p.setTextSize((float) textSize); + w = (int) p.measureText(string); + // Round width up to even already here to avoid annoyances from odd-width 16-bit textures + // which OpenGL does not like - each line must be 4-byte aligned w = (w + 5) & ~1; } else { w = 1; } - int h = (int)(p.descent() - p.ascent() + 2.0f); + int h = (int) (p.descent() - p.ascent() + 2.0f); Point p = new Point(); p.x = w; p.y = h; return p; } + private static Point measure(String string, double textSize) { String lines[] = string.replaceAll("\\r", "").split("\n"); Point total = new Point(); @@ -50,13 +53,15 @@ public class TextRenderer { Point sz = measureLine(line, textSize); total.x = Math.max(sz.x, total.x); } - total.y = (int)(p.descent() - p.ascent()) * lines.length + 2; + total.y = (int) (p.descent() - p.ascent()) * lines.length + 2; return total; } + public static int measureText(String string, double textSize) { Point s = measure(string, textSize); return (s.x << 16) | s.y; } + public static int[] renderText(String string, double textSize) { Point s = measure(string, textSize); @@ -79,7 +84,7 @@ public class TextRenderer { y += p.descent() - p.ascent(); } - int [] pixels = new int[w * h]; + int[] pixels = new int[w * h]; bmp.getPixels(pixels, 0, w, 0, 0, w, h); bmp.recycle(); return pixels; From 3380bbb0c6b6c81fb64e16c3fae89f0e29d076b7 Mon Sep 17 00:00:00 2001 From: zhang wei Date: Mon, 4 Jun 2018 22:00:29 +0800 Subject: [PATCH 081/301] save cache before clear --- GPU/GLES/GPU_GLES.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 1dd7611211..6b4864c2c6 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -128,13 +128,15 @@ GPU_GLES::~GPU_GLES() { // If we're here during app shutdown (exiting the Windows app in-game, for example) // everything should already be cleared since DeviceLost has been run. + if (!shaderCachePath_.empty() && draw_) { + shaderManagerGL_->Save(shaderCachePath_); + } + framebufferManagerGL_->DestroyAllFBOs(); shaderManagerGL_->ClearCache(true); depalShaderCache_.Clear(); fragmentTestCache_.Clear(); - if (!shaderCachePath_.empty() && draw_) { - shaderManagerGL_->Save(shaderCachePath_); - } + delete shaderManagerGL_; shaderManagerGL_ = nullptr; delete framebufferManagerGL_; From 6db008fc02c3d748ec41d738f1f5c3cfac710826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 4 Jun 2018 22:02:10 +0200 Subject: [PATCH 082/301] Update lang submodule again --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 97df2d45d3..1153a42930 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 97df2d45d35c5a57a615727148f7221c1fefe87f +Subproject commit 1153a4293006db9d2a6e701df72df843b98234b5 From f2e34767416be82d1a2bd4f2070817188679c307 Mon Sep 17 00:00:00 2001 From: zhang wei Date: Tue, 5 Jun 2018 10:39:40 +0800 Subject: [PATCH 083/301] triangles --- GPU/Common/IndexGenerator.cpp | 24 ++++++++++++++---------- GPU/Common/IndexGenerator.h | 4 ++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/GPU/Common/IndexGenerator.cpp b/GPU/Common/IndexGenerator.cpp index c481bfe957..9c1b13d42d 100644 --- a/GPU/Common/IndexGenerator.cpp +++ b/GPU/Common/IndexGenerator.cpp @@ -41,7 +41,7 @@ void IndexGenerator::AddPrim(int prim, int vertexCount, bool clockwise) { case GE_PRIM_POINTS: AddPoints(vertexCount); break; case GE_PRIM_LINES: AddLineList(vertexCount); break; case GE_PRIM_LINE_STRIP: AddLineStrip(vertexCount); break; - case GE_PRIM_TRIANGLES: AddList(vertexCount); break; + case GE_PRIM_TRIANGLES: AddList(vertexCount, clockwise); break; case GE_PRIM_TRIANGLE_STRIP: AddStrip(vertexCount, clockwise); break; case GE_PRIM_TRIANGLE_FAN: AddFan(vertexCount, clockwise); break; case GE_PRIM_RECTANGLES: AddRectangles(vertexCount); break; // Same @@ -61,13 +61,15 @@ void IndexGenerator::AddPoints(int numVerts) { seenPrims_ |= 1 << GE_PRIM_POINTS; } -void IndexGenerator::AddList(int numVerts) { +void IndexGenerator::AddList(int numVerts, bool clockwise) { u16 *outInds = inds_; const int startIndex = index_; + const int v1 = clockwise ? 1 : 2; + const int v2 = clockwise ? 2 : 1; for (int i = 0; i < numVerts; i += 3) { *outInds++ = startIndex + i; - *outInds++ = startIndex + i + 1; - *outInds++ = startIndex + i + 2; + *outInds++ = startIndex + i + v1; + *outInds++ = startIndex + i + v2; } inds_ = outInds; // ignore overflow verts @@ -212,7 +214,7 @@ void IndexGenerator::TranslateLineStrip(int numInds, const ITypeLE *inds, int in } template -void IndexGenerator::TranslateList(int numInds, const ITypeLE *inds, int indexOffset) { +void IndexGenerator::TranslateList(int numInds, const ITypeLE *inds, int indexOffset, bool clockwise) { indexOffset = index_ - indexOffset; // We only bother doing this minor optimization in triangle list, since it's by far the most // common operation that can benefit. @@ -224,10 +226,12 @@ void IndexGenerator::TranslateList(int numInds, const ITypeLE *inds, int indexOf u16 *outInds = inds_; int numTris = numInds / 3; // Round to whole triangles numInds = numTris * 3; + const int v1 = clockwise ? 1 : 2; + const int v2 = clockwise ? 2 : 1; for (int i = 0; i < numInds; i += 3) { *outInds++ = indexOffset + inds[i]; - *outInds++ = indexOffset + inds[i + 1]; - *outInds++ = indexOffset + inds[i + 2]; + *outInds++ = indexOffset + inds[i + v1]; + *outInds++ = indexOffset + inds[i + v2]; } inds_ = outInds; count_ += numInds; @@ -295,7 +299,7 @@ void IndexGenerator::TranslatePrim(int prim, int numInds, const u8 *inds, int in case GE_PRIM_POINTS: TranslatePoints(numInds, inds, indexOffset); break; case GE_PRIM_LINES: TranslateLineList(numInds, inds, indexOffset); break; case GE_PRIM_LINE_STRIP: TranslateLineStrip(numInds, inds, indexOffset); break; - case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset); break; + case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_TRIANGLE_STRIP: TranslateStrip(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_RECTANGLES: TranslateRectangles(numInds, inds, indexOffset); break; // Same @@ -307,7 +311,7 @@ void IndexGenerator::TranslatePrim(int prim, int numInds, const u16_le *inds, in case GE_PRIM_POINTS: TranslatePoints(numInds, inds, indexOffset); break; case GE_PRIM_LINES: TranslateLineList(numInds, inds, indexOffset); break; case GE_PRIM_LINE_STRIP: TranslateLineStrip(numInds, inds, indexOffset); break; - case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset); break; + case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_TRIANGLE_STRIP: TranslateStrip(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_RECTANGLES: TranslateRectangles(numInds, inds, indexOffset); break; // Same @@ -319,7 +323,7 @@ void IndexGenerator::TranslatePrim(int prim, int numInds, const u32_le *inds, in case GE_PRIM_POINTS: TranslatePoints(numInds, inds, indexOffset); break; case GE_PRIM_LINES: TranslateLineList(numInds, inds, indexOffset); break; case GE_PRIM_LINE_STRIP: TranslateLineStrip(numInds, inds, indexOffset); break; - case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset); break; + case GE_PRIM_TRIANGLES: TranslateList(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_TRIANGLE_STRIP: TranslateStrip(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_TRIANGLE_FAN: TranslateFan(numInds, inds, indexOffset, clockwise); break; case GE_PRIM_RECTANGLES: TranslateRectangles(numInds, inds, indexOffset); break; // Same diff --git a/GPU/Common/IndexGenerator.h b/GPU/Common/IndexGenerator.h index 877808700c..6fb24930ee 100644 --- a/GPU/Common/IndexGenerator.h +++ b/GPU/Common/IndexGenerator.h @@ -75,7 +75,7 @@ private: // Points (why index these? code simplicity) void AddPoints(int numVerts); // Triangles - void AddList(int numVerts); + void AddList(int numVerts, bool clockwise); void AddStrip(int numVerts, bool clockwise); void AddFan(int numVerts, bool clockwise); // Lines @@ -88,7 +88,7 @@ private: template void TranslatePoints(int numVerts, const ITypeLE *inds, int indexOffset); template - void TranslateList(int numVerts, const ITypeLE *inds, int indexOffset); + void TranslateList(int numVerts, const ITypeLE *inds, int indexOffset, bool clockwise); template inline void TranslateLineList(int numVerts, const ITypeLE *inds, int indexOffset); template From 9a610c8a012e73b7d2c6677f48d24f3e185e61c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 5 Jun 2018 19:58:41 +0200 Subject: [PATCH 084/301] Update to 1.6.3 --- README.md | 5 +++++ android/AndroidManifest.xml | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 357cebcd79..7affcbac26 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,11 @@ For build instructions and other development tutorials, see [the wiki](https://g For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games). +What's new in 1.6.3 +------------------- +* Crashfixes, task switching and one in Phantasy Star Portable +* Improve graphics in PoP on some devices + What's new in 1.6.1, 1.6.2 -------------------------- * Crashfixes diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index a908a45c9f..9af9bd0fd6 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,9 +2,9 @@ - + android:versionCode="16030000" + android:versionName="1.6.3.0"> + Date: Tue, 5 Jun 2018 20:27:54 -0700 Subject: [PATCH 085/301] SDL: Properly try multiple GL versions. Thanks go to hissingshark. --- SDL/SDLGLGraphicsContext.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 3693c77c5a..0939fe7d55 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -45,8 +45,9 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std: window = SDL_CreateWindow("PPSSPP", x,y, pixel_xres, pixel_yres, mode); if (!window) { - NativeShutdown(); - fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); + // Definitely don't shutdown here: we'll keep trying more GL versions. + fprintf(stderr, "SDL_CreateWindow failed for GL %d.%d: %s\n", ver.major, ver.minor, SDL_GetError()); + // Skip the DestroyWindow. continue; } From 78a41980dfd7da1216dc4111acbd9652741481dc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 5 Jun 2018 20:39:56 -0700 Subject: [PATCH 086/301] SDL: Use fullscreen for mobile/GLES. Also, allow resize for Vulkan outside mobile. Thanks go to hissingshark. --- SDL/SDLGLGraphicsContext.cpp | 7 ++----- SDL/SDLMain.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 0939fe7d55..fff1defd68 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -23,11 +23,8 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std: #endif }; -#ifdef USING_GLES2 - mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN; -#else - mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; -#endif + mode |= SDL_WINDOW_OPENGL; + SDL_GLContext glContext = nullptr; for (size_t i = 0; i < ARRAY_SIZE(attemptVersions); ++i) { const auto &ver = attemptVersions[i]; diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 6517748c58..cfae2330c3 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -555,6 +555,13 @@ int main(int argc, char *argv[]) { mode |= SDL_WINDOW_FULLSCREEN_DESKTOP; } + // If we're on mobile, don't try for windowed either. +#if defined(USING_GLES2) || defined(MOBILE_DEVICE) + mode |= SDL_WINDOW_FULLSCREEN; +#else + mode |= SDL_WINDOW_RESIZABLE; +#endif + if (mode & SDL_WINDOW_FULLSCREEN_DESKTOP) { pixel_xres = g_DesktopWidth; pixel_yres = g_DesktopHeight; From a7023ac54248f29211634ce31c67bbcb363ed9fa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 5 Jun 2018 23:02:50 -0700 Subject: [PATCH 087/301] SDL: Move EGL init code into SDLGLGraphicsContext. It should really be cleaned up, but I'm not sure if EGL_Open can be moved later and don't currently have a setup to test, so I'm just cargo culting. --- SDL/SDLGLGraphicsContext.cpp | 135 +++++++++++++++++++++++++++++++++++ SDL/SDLGLGraphicsContext.h | 3 + SDL/SDLMain.cpp | 133 ---------------------------------- 3 files changed, 138 insertions(+), 133 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index fff1defd68..b474da6497 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -6,8 +6,143 @@ #include "gfx_es2/gpu_features.h" #include "thin3d/thin3d_create.h" +#if defined(USING_EGL) +#include "EGL/egl.h" +#endif + class GLRenderManager; +#if defined(USING_EGL) + +// TODO: Move these into the class. +static EGLDisplay g_eglDisplay = NULL; +static EGLContext g_eglContext = NULL; +static EGLSurface g_eglSurface = NULL; +#ifdef USING_FBDEV +static EGLNativeDisplayType g_Display = NULL; +#else +static Display* g_Display = NULL; +#endif +static NativeWindowType g_Window = (NativeWindowType)NULL; + +int8_t CheckEGLErrors(const std::string& file, uint16_t line) { + EGLenum error; + std::string errortext; + + error = eglGetError(); + switch (error) + { + case EGL_SUCCESS: case 0: return 0; + case EGL_NOT_INITIALIZED: errortext = "EGL_NOT_INITIALIZED"; break; + case EGL_BAD_ACCESS: errortext = "EGL_BAD_ACCESS"; break; + case EGL_BAD_ALLOC: errortext = "EGL_BAD_ALLOC"; break; + case EGL_BAD_ATTRIBUTE: errortext = "EGL_BAD_ATTRIBUTE"; break; + case EGL_BAD_CONTEXT: errortext = "EGL_BAD_CONTEXT"; break; + case EGL_BAD_CONFIG: errortext = "EGL_BAD_CONFIG"; break; + case EGL_BAD_CURRENT_SURFACE: errortext = "EGL_BAD_CURRENT_SURFACE"; break; + case EGL_BAD_DISPLAY: errortext = "EGL_BAD_DISPLAY"; break; + case EGL_BAD_SURFACE: errortext = "EGL_BAD_SURFACE"; break; + case EGL_BAD_MATCH: errortext = "EGL_BAD_MATCH"; break; + case EGL_BAD_PARAMETER: errortext = "EGL_BAD_PARAMETER"; break; + case EGL_BAD_NATIVE_PIXMAP: errortext = "EGL_BAD_NATIVE_PIXMAP"; break; + case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break; + default: errortext = "unknown"; break; + } + printf( "ERROR: EGL Error detected in file %s at line %d: %s (0x%X)\n", file.c_str(), line, errortext.c_str(), error ); + return 1; +} + +#define EGL_ERROR(str, check) { \ + if (check) CheckEGLErrors( __FILE__, __LINE__ ); \ + printf("EGL ERROR: " str "\n"); \ + return 1; \ + } + +int8_t EGL_Open() { +#ifdef USING_FBDEV + g_Display = ((EGLNativeDisplayType)0); +#else + if ((g_Display = XOpenDisplay(NULL)) == NULL) + EGL_ERROR("Unable to get display!", false); +#endif + if ((g_eglDisplay = eglGetDisplay((NativeDisplayType)g_Display)) == EGL_NO_DISPLAY) + EGL_ERROR("Unable to create EGL display.", true); + if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE) + EGL_ERROR("Unable to initialize EGL display.", true); + return 0; +} + +int8_t EGL_Init() { + EGLConfig g_eglConfig; + EGLint g_numConfigs = 0; + EGLint attrib_list[]= { + // TODO: Should cycle through fallbacks, like on Android +#ifdef USING_FBDEV + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, +#endif + EGL_DEPTH_SIZE, 16, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, +#ifdef USING_GLES2 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, +#endif + EGL_SAMPLE_BUFFERS, 0, + EGL_SAMPLES, 0, + EGL_NONE}; + + const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + EGLBoolean result = eglChooseConfig(g_eglDisplay, attrib_list, &g_eglConfig, 1, &g_numConfigs); + if (result != EGL_TRUE || g_numConfigs == 0) EGL_ERROR("Unable to query for available configs.", true); + + g_eglContext = eglCreateContext(g_eglDisplay, g_eglConfig, NULL, attributes ); + if (g_eglContext == EGL_NO_CONTEXT) EGL_ERROR("Unable to create GLES context!", true); + +#if !defined(USING_FBDEV) && !defined(__APPLE__) + //Get the SDL window handle + SDL_SysWMinfo sysInfo; //Will hold our Window information + SDL_VERSION(&sysInfo.version); //Set SDL version + g_Window = (NativeWindowType)sysInfo.info.x11.window; +#else + g_Window = (NativeWindowType)NULL; +#endif + g_eglSurface = eglCreateWindowSurface(g_eglDisplay, g_eglConfig, g_Window, 0); + if (g_eglSurface == EGL_NO_SURFACE) + EGL_ERROR("Unable to create EGL surface!", true); + + if (eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext) != EGL_TRUE) + EGL_ERROR("Unable to make GLES context current.", true); + + return 0; +} + +void EGL_Close() { + if (g_eglDisplay != NULL) { + eglMakeCurrent(g_eglDisplay, NULL, NULL, EGL_NO_CONTEXT); + if (g_eglContext != NULL) { + eglDestroyContext(g_eglDisplay, g_eglContext); + } + if (g_eglSurface != NULL) { + eglDestroySurface(g_eglDisplay, g_eglSurface); + } + eglTerminate(g_eglDisplay); + g_eglDisplay = NULL; + } + if (g_Display != NULL) { +#if !defined(USING_FBDEV) + XCloseDisplay(g_Display); +#endif + g_Display = NULL; + } + g_eglSurface = NULL; + g_eglContext = NULL; +} + +#endif // USING_EGL + // Returns 0 on success. int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std::string *error_message) { struct GLVersionPair { diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index 2695ebf64d..052c6e7ac8 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -7,6 +7,9 @@ #include "gfx/gl_common.h" #include "Common/GraphicsContext.h" +// TODO: Move this to a better place. +void EGL_Open(); + class SDLGLGraphicsContext : public DummyGraphicsContext { public: SDLGLGraphicsContext() { diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index cfae2330c3..081d924009 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -48,10 +48,6 @@ SDLJoystick *joystick = NULL; #include #endif -#if defined(USING_EGL) -#include "EGL/egl.h" -#endif - #include "Core/System.h" #include "Core/Core.h" #include "Core/Config.h" @@ -70,135 +66,6 @@ static int g_QuitRequested = 0; static int g_DesktopWidth = 0; static int g_DesktopHeight = 0; -#if defined(USING_EGL) - -static EGLDisplay g_eglDisplay = NULL; -static EGLContext g_eglContext = NULL; -static EGLSurface g_eglSurface = NULL; -#ifdef USING_FBDEV -static EGLNativeDisplayType g_Display = NULL; -#else -static Display* g_Display = NULL; -#endif -static NativeWindowType g_Window = (NativeWindowType)NULL; - -int8_t CheckEGLErrors(const std::string& file, uint16_t line) { - EGLenum error; - std::string errortext; - - error = eglGetError(); - switch (error) - { - case EGL_SUCCESS: case 0: return 0; - case EGL_NOT_INITIALIZED: errortext = "EGL_NOT_INITIALIZED"; break; - case EGL_BAD_ACCESS: errortext = "EGL_BAD_ACCESS"; break; - case EGL_BAD_ALLOC: errortext = "EGL_BAD_ALLOC"; break; - case EGL_BAD_ATTRIBUTE: errortext = "EGL_BAD_ATTRIBUTE"; break; - case EGL_BAD_CONTEXT: errortext = "EGL_BAD_CONTEXT"; break; - case EGL_BAD_CONFIG: errortext = "EGL_BAD_CONFIG"; break; - case EGL_BAD_CURRENT_SURFACE: errortext = "EGL_BAD_CURRENT_SURFACE"; break; - case EGL_BAD_DISPLAY: errortext = "EGL_BAD_DISPLAY"; break; - case EGL_BAD_SURFACE: errortext = "EGL_BAD_SURFACE"; break; - case EGL_BAD_MATCH: errortext = "EGL_BAD_MATCH"; break; - case EGL_BAD_PARAMETER: errortext = "EGL_BAD_PARAMETER"; break; - case EGL_BAD_NATIVE_PIXMAP: errortext = "EGL_BAD_NATIVE_PIXMAP"; break; - case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break; - default: errortext = "unknown"; break; - } - printf( "ERROR: EGL Error detected in file %s at line %d: %s (0x%X)\n", file.c_str(), line, errortext.c_str(), error ); - return 1; -} -#define EGL_ERROR(str, check) { \ - if (check) CheckEGLErrors( __FILE__, __LINE__ ); \ - printf("EGL ERROR: " str "\n"); \ - return 1; \ - } - -int8_t EGL_Open() { -#ifdef USING_FBDEV - g_Display = ((EGLNativeDisplayType)0); -#else - if ((g_Display = XOpenDisplay(NULL)) == NULL) - EGL_ERROR("Unable to get display!", false); -#endif - if ((g_eglDisplay = eglGetDisplay((NativeDisplayType)g_Display)) == EGL_NO_DISPLAY) - EGL_ERROR("Unable to create EGL display.", true); - if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE) - EGL_ERROR("Unable to initialize EGL display.", true); - return 0; -} - -int8_t EGL_Init() { - EGLConfig g_eglConfig; - EGLint g_numConfigs = 0; - EGLint attrib_list[]= { - // TODO: Should cycle through fallbacks, like on Android -#ifdef USING_FBDEV - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, -#endif - EGL_DEPTH_SIZE, 16, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, -#ifdef USING_GLES2 - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, -#else - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, -#endif - EGL_SAMPLE_BUFFERS, 0, - EGL_SAMPLES, 0, - EGL_NONE}; - - const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - - EGLBoolean result = eglChooseConfig(g_eglDisplay, attrib_list, &g_eglConfig, 1, &g_numConfigs); - if (result != EGL_TRUE || g_numConfigs == 0) EGL_ERROR("Unable to query for available configs.", true); - - g_eglContext = eglCreateContext(g_eglDisplay, g_eglConfig, NULL, attributes ); - if (g_eglContext == EGL_NO_CONTEXT) EGL_ERROR("Unable to create GLES context!", true); - -#if !defined(USING_FBDEV) && !defined(__APPLE__) - //Get the SDL window handle - SDL_SysWMinfo sysInfo; //Will hold our Window information - SDL_VERSION(&sysInfo.version); //Set SDL version - g_Window = (NativeWindowType)sysInfo.info.x11.window; -#else - g_Window = (NativeWindowType)NULL; -#endif - g_eglSurface = eglCreateWindowSurface(g_eglDisplay, g_eglConfig, g_Window, 0); - if (g_eglSurface == EGL_NO_SURFACE) - EGL_ERROR("Unable to create EGL surface!", true); - - if (eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext) != EGL_TRUE) - EGL_ERROR("Unable to make GLES context current.", true); - - return 0; -} - -void EGL_Close() { - if (g_eglDisplay != NULL) { - eglMakeCurrent(g_eglDisplay, NULL, NULL, EGL_NO_CONTEXT); - if (g_eglContext != NULL) { - eglDestroyContext(g_eglDisplay, g_eglContext); - } - if (g_eglSurface != NULL) { - eglDestroySurface(g_eglDisplay, g_eglSurface); - } - eglTerminate(g_eglDisplay); - g_eglDisplay = NULL; - } - if (g_Display != NULL) { -#if !defined(USING_FBDEV) - XCloseDisplay(g_Display); -#endif - g_Display = NULL; - } - g_eglSurface = NULL; - g_eglContext = NULL; -} -#endif - - int getDisplayNumber(void) { int displayNumber = 0; char * displayNumberStr; From b037efdb55d4ccc00130931b4ffea3dad8271037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Apr 2018 09:56:37 +0200 Subject: [PATCH 088/301] If there are multiple Vulkan devices, show a setting to allow the user to choose. --- Common/Vulkan/VulkanContext.cpp | 13 +++++- Common/Vulkan/VulkanContext.h | 13 ++++-- Core/Config.cpp | 1 + Core/Config.h | 3 ++ GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- GPU/Vulkan/GPU_Vulkan.cpp | 10 ++-- GPU/Vulkan/PipelineManagerVulkan.cpp | 2 +- GPU/Vulkan/ShaderManagerVulkan.cpp | 4 +- GPU/Vulkan/TextureCacheVulkan.cpp | 2 +- UI/GameSettingsScreen.cpp | 13 ++++++ Windows/GPU/WindowsVulkanContext.cpp | 7 ++- ext/native/thin3d/VulkanQueueRunner.cpp | 2 +- ext/native/thin3d/VulkanRenderManager.cpp | 2 +- ext/native/thin3d/thin3d.h | 2 + ext/native/thin3d/thin3d_create.h | 2 +- ext/native/thin3d/thin3d_vulkan.cpp | 19 +++++--- ext/native/ui/ui_screen.cpp | 3 ++ ext/native/ui/ui_screen.h | 57 +++++++++++++++++++---- 18 files changed, 124 insertions(+), 33 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index 5b3da0b7d7..ffe7ba5a3f 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -213,6 +213,7 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) { assert(gpu_count > 0); physical_devices_.resize(gpu_count); + physicalDeviceProperties_.resize(gpu_count); res = vkEnumeratePhysicalDevices(instance_, &gpu_count, physical_devices_.data()); if (res != VK_SUCCESS) { init_error_ = "Failed to enumerate physical devices"; @@ -221,6 +222,9 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) { return res; } + for (uint32_t i = 0; i < gpu_count; i++) { + vkGetPhysicalDeviceProperties(physical_devices_[i], &physicalDeviceProperties_[i]); + } return VK_SUCCESS; } @@ -417,6 +421,14 @@ bool VulkanContext::CheckLayers(const std::vector &layer_props, return true; } +int VulkanContext::GetPhysicalDeviceByName(std::string name) { + for (size_t i = 0; i < physical_devices_.size(); i++) { + if (physicalDeviceProperties_[i].deviceName == name) + return (int)i; + } + return -1; +} + int VulkanContext::GetBestPhysicalDevice() { // Rules: Prefer discrete over embedded. // Prefer nVidia over Intel. @@ -490,7 +502,6 @@ void VulkanContext::ChooseDevice(int physical_device) { // This is as good a place as any to do this vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties); - vkGetPhysicalDeviceProperties(physical_devices_[physical_device_], &gpu_props); // Optional features vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &featuresAvailable_); diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 6466f7b390..e58824cd26 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -128,6 +128,7 @@ public: void DestroyInstance(); int GetBestPhysicalDevice(); + int GetPhysicalDeviceByName(std::string name); void ChooseDevice(int physical_device); bool EnableDeviceExtension(const char *extension); VkResult CreateDevice(); @@ -173,6 +174,12 @@ public: VkPhysicalDevice GetPhysicalDevice(int n = 0) const { return physical_devices_[n]; } + int GetCurrentPhysicalDevice() const { + return physical_device_; + } + int GetNumPhysicalDevices() const { + return (int)physical_devices_.size(); + } VkQueue GetGraphicsQueue() const { return gfx_queue_; @@ -182,8 +189,8 @@ public: return graphics_queue_family_index_; } - const VkPhysicalDeviceProperties &GetPhysicalDeviceProperties() { - return gpu_props; + const VkPhysicalDeviceProperties &GetPhysicalDeviceProperties(int i) const { + return physicalDeviceProperties_[i]; } VkResult GetInstanceLayerExtensionList(const char *layerName, std::vector &extensions); @@ -281,7 +288,7 @@ private: int physical_device_ = -1; uint32_t graphics_queue_family_index_ = -1; - VkPhysicalDeviceProperties gpu_props{}; + std::vector physicalDeviceProperties_{}; std::vector queue_props; VkPhysicalDeviceMemoryProperties memory_properties{}; diff --git a/Core/Config.cpp b/Core/Config.cpp index e2bde296d9..9350941f3c 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -507,6 +507,7 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("CardboardYShift", &g_Config.iCardboardXShift, 0, true, true), ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true), ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend), + ConfigSetting("VulkanDevice", &g_Config.VulkanDevice, "", true, false), ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, &DefaultRenderingMode, true, true), ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true), ReportedConfigSetting("HardwareTransform", &g_Config.bHardwareTransform, true, true, true), diff --git a/Core/Config.h b/Core/Config.h index 466624ce67..3a022646c9 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -153,6 +153,9 @@ public: // GFX int iGPUBackend; + // We have separate device parameters for each backend so it doesn't get erased if you switch backends. + // If not set, will use the "best" device. + std::string VulkanDevice; bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend bool bSoftwareSkinning; // may speed up some games diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index b8c62f596e..76643d6066 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -1007,7 +1007,7 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po float color[4]; }; - int ssboAlignment = vulkan_->GetPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment; + int ssboAlignment = vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).limits.minStorageBufferOffsetAlignment; uint8_t *data = (uint8_t *)push_->PushAligned(size * sizeof(TessData), &offset_, &buf_, ssboAlignment); range_ = size * sizeof(TessData); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 5790fe146e..69cd57feb6 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -175,7 +175,7 @@ GPU_Vulkan::~GPU_Vulkan() { void GPU_Vulkan::CheckGPUFeatures() { uint32_t features = 0; - switch (vulkan_->GetPhysicalDeviceProperties().vendorID) { + switch (vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID) { case VULKAN_VENDOR_AMD: // Accurate depth is required on AMD (due to reverse-Z driver bug) so we ignore the compat flag to disable it on those. See #9545 features |= GPU_SUPPORTS_ACCURATE_DEPTH; @@ -183,7 +183,7 @@ void GPU_Vulkan::CheckGPUFeatures() { case VULKAN_VENDOR_ARM: // Also required on older ARM Mali drivers, like the one on many Galaxy S7. if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || - vulkan_->GetPhysicalDeviceProperties().driverVersion <= VK_MAKE_VERSION(428, 811, 2674)) { + vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).driverVersion <= VK_MAKE_VERSION(428, 811, 2674)) { features |= GPU_SUPPORTS_ACCURATE_DEPTH; } break; @@ -212,7 +212,7 @@ void GPU_Vulkan::CheckGPUFeatures() { features |= GPU_SUPPORTS_DEPTH_CLAMP; } if (vulkan_->GetFeaturesEnabled().dualSrcBlend) { - switch (vulkan_->GetPhysicalDeviceProperties().vendorID) { + switch (vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID) { // We thought we had a bug here on nVidia but turns out we accidentally #ifdef-ed out crucial // code on Android. case VULKAN_VENDOR_INTEL: @@ -220,7 +220,7 @@ void GPU_Vulkan::CheckGPUFeatures() { break; case VULKAN_VENDOR_AMD: // See issue #10074, and also #10065 (AMD) and #10109 for the choice of the driver version to check for - if (vulkan_->GetPhysicalDeviceProperties().driverVersion >= 0x00407000) + if (vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).driverVersion >= 0x00407000) features |= GPU_SUPPORTS_DUALSOURCE_BLEND; break; default: @@ -310,7 +310,7 @@ void GPU_Vulkan::EndHostFrame() { // Needs to be called on GPU thread, not reporting thread. void GPU_Vulkan::BuildReportingInfo() { - const auto &props = vulkan_->GetPhysicalDeviceProperties(); + const auto &props = vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()); const auto &features = vulkan_->GetFeaturesAvailable(); #define CHECK_BOOL_FEATURE(n) do { if (features.n) { featureNames += ", " #n; } } while (false) diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 4498dedb78..83cdf0ee28 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -676,7 +676,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha WARN_LOG(G3D, "Bad Vulkan pipeline cache header - ignoring"); return false; } - if (0 != memcmp(header->uuid, vulkan_->GetPhysicalDeviceProperties().pipelineCacheUUID, VK_UUID_SIZE)) { + if (0 != memcmp(header->uuid, vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).pipelineCacheUUID, VK_UUID_SIZE)) { // Wrong hardware/driver/etc. WARN_LOG(G3D, "Bad Vulkan pipeline cache UUID - ignoring"); return false; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 2168eae5b1..3a06fc2d0d 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -159,7 +159,7 @@ std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) cons ShaderManagerVulkan::ShaderManagerVulkan(VulkanContext *vulkan) : vulkan_(vulkan), lastVShader_(nullptr), lastFShader_(nullptr), fsCache_(16), vsCache_(16) { codeBuffer_ = new char[16384]; - uboAlignment_ = vulkan_->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment; + uboAlignment_ = vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).limits.minUniformBufferOffsetAlignment; memset(&ub_base, 0, sizeof(ub_base)); memset(&ub_lights, 0, sizeof(ub_lights)); memset(&ub_bones, 0, sizeof(ub_bones)); @@ -176,7 +176,7 @@ ShaderManagerVulkan::~ShaderManagerVulkan() { void ShaderManagerVulkan::DeviceRestore(VulkanContext *vulkan) { vulkan_ = vulkan; - uboAlignment_ = vulkan_->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment; + uboAlignment_ = vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).limits.minUniformBufferOffsetAlignment; } void ShaderManagerVulkan::Clear() { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 8553985564..cc6d6dedaa 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -683,7 +683,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { uint32_t bufferOffset; VkBuffer texBuf; // nvidia returns 1 but that can't be healthy... let's align by 16 as a minimum. - int pushAlignment = std::max(16, (int)vulkan_->GetPhysicalDeviceProperties().limits.optimalBufferCopyOffsetAlignment); + int pushAlignment = std::max(16, (int)vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).limits.optimalBufferCopyOffsetAlignment); void *data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment); if (replaced.Valid()) { replaced.Load(i, data, stride); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 904477697e..be1d8ef3e6 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -202,6 +202,19 @@ void GameSettingsScreen::CreateViews() { renderingBackendChoice->HideChoice(3); } #endif + Draw::DrawContext *draw = screenManager()->getDrawContext(); + if (draw->GetDeviceList().size() > 0) { + // Some backends don't support switching so no point in showing multiple devices. + std::string *deviceNameSetting = nullptr; + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { + deviceNameSetting = &g_Config.VulkanDevice; + } + + if (deviceNameSetting) { + PopupMultiChoiceDynamic *deviceChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(deviceNameSetting, gr->T("Device"), draw->GetDeviceList(), nullptr, screenManager())); + deviceChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend); + } + } static const char *renderingMode[] = { "Non-Buffered Rendering", "Buffered Rendering"}; PopupMultiChoice *renderingModeChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iRenderingMode, gr->T("Mode"), renderingMode, 0, ARRAY_SIZE(renderingMode), gr->GetName(), screenManager())); diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 0e71ace543..3ec3d08207 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -109,7 +109,12 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m g_Vulkan = nullptr; return false; } - g_Vulkan->ChooseDevice(g_Vulkan->GetBestPhysicalDevice()); + int deviceNum = g_Vulkan->GetPhysicalDeviceByName(g_Config.VulkanDevice); + if (deviceNum < 0) { + deviceNum = g_Vulkan->GetBestPhysicalDevice(); + g_Config.VulkanDevice = g_Vulkan->GetPhysicalDeviceProperties(deviceNum).deviceName; + } + g_Vulkan->ChooseDevice(deviceNum); if (g_Vulkan->EnableDeviceExtension(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME)) { supportsDedicatedAlloc_ = true; } diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 1be6960540..ed71016192 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -850,7 +850,7 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step // See pull request #10723. bool maliBugWorkaround = step.render.numDraws == 0 && step.render.color == VKRRenderPassAction::CLEAR && - vulkan_->GetPhysicalDeviceProperties().driverVersion == 0xaa9c4b29; + vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).driverVersion == 0xaa9c4b29; if (maliBugWorkaround) { TransitionImageLayout2(cmd, step.render.framebuffer->color.image, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT, fb->color.layout, VK_IMAGE_LAYOUT_GENERAL, diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 325d0f6851..e593442798 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -136,7 +136,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan queueRunner_.CreateDeviceObjects(); // Temporary AMD hack for issue #10097 - if (vulkan_->GetPhysicalDeviceProperties().vendorID == VULKAN_VENDOR_AMD) { + if (vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID == VULKAN_VENDOR_AMD) { useThread_ = false; } } diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 323850fe43..3c95516ce0 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -491,6 +491,7 @@ struct DeviceCaps { bool framebufferBlitSupported; bool framebufferDepthCopySupported; bool framebufferDepthBlitSupported; + std::string deviceName; // The device name to use when creating the thin3d context, to get the same one. }; struct TextureDesc { @@ -532,6 +533,7 @@ public: virtual uint32_t GetDataFormatSupport(DataFormat fmt) const = 0; virtual std::vector GetFeatureList() const { return std::vector(); } virtual std::vector GetExtensionList() const { return std::vector(); } + virtual std::vector GetDeviceList() const { return std::vector(); } virtual uint32_t GetSupportedShaderLanguages() const = 0; diff --git a/ext/native/thin3d/thin3d_create.h b/ext/native/thin3d/thin3d_create.h index 3d4ea0f1a4..9d2ea4a844 100644 --- a/ext/native/thin3d/thin3d_create.h +++ b/ext/native/thin3d/thin3d_create.h @@ -33,6 +33,6 @@ DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapt DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd); #endif -DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool split); +DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool splitSubmit); } // namespace Draw diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index c71ff4f716..065bf4c7f0 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -261,7 +261,7 @@ public: // Returns the binding offset, and the VkBuffer to bind. size_t PushUBO(VulkanPushBuffer *buf, VulkanContext *vulkan, VkBuffer *vkbuf) { - return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment, vkbuf); + return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).limits.minUniformBufferOffsetAlignment, vkbuf); } int GetUniformLoc(const char *name); @@ -350,6 +350,13 @@ public: const DeviceCaps &GetDeviceCaps() const override { return caps_; } + std::vector GetDeviceList() const override { + std::vector list; + for (int i = 0; i < vulkan_->GetNumPhysicalDevices(); i++) { + list.push_back(vulkan_->GetPhysicalDeviceProperties(i).deviceName); + } + return list; + } uint32_t GetSupportedShaderLanguages() const override { return (uint32_t)ShaderLanguage::GLSL_VULKAN | (uint32_t)ShaderLanguage::SPIRV_VULKAN; } @@ -446,13 +453,13 @@ public: // TODO: Make these actually query the right information switch (info) { case APINAME: return "Vulkan"; - case VENDORSTRING: return vulkan_->GetPhysicalDeviceProperties().deviceName; - case VENDOR: return VulkanVendorString(vulkan_->GetPhysicalDeviceProperties().vendorID); - case DRIVER: return FormatDriverVersion(vulkan_->GetPhysicalDeviceProperties()); + case VENDORSTRING: return vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).deviceName; + case VENDOR: return VulkanVendorString(vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID); + case DRIVER: return FormatDriverVersion(vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice())); case SHADELANGVERSION: return "N/A";; case APIVERSION: { - uint32_t ver = vulkan_->GetPhysicalDeviceProperties().apiVersion; + uint32_t ver = vulkan_->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).apiVersion; return StringFromFormat("%d.%d.%d", ver >> 22, (ver >> 12) & 0x3ff, ver & 0xfff); } default: return "?"; @@ -746,7 +753,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case. caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. - switch (vulkan->GetPhysicalDeviceProperties().vendorID) { + switch (vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDevice()).vendorID) { case VULKAN_VENDOR_AMD: caps_.vendor = GPUVendor::VENDOR_AMD; break; case VULKAN_VENDOR_ARM: caps_.vendor = GPUVendor::VENDOR_ARM; break; case VULKAN_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break; diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index e6830635f3..b4a4edb76b 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -444,6 +444,8 @@ void PopupMultiChoice::Update() { } void PopupMultiChoice::UpdateText() { + if (!choices_) + return; I18NCategory *category = GetI18NCategory(category_); // Clamp the value to be safe. if (*value_ < minVal_ || *value_ > minVal_ + numChoices_ - 1) { @@ -466,6 +468,7 @@ void PopupMultiChoice::ChoiceCallback(int num) { if (restoreFocus_) { SetFocusedView(this); } + PostChoiceCallback(num); } } diff --git a/ext/native/ui/ui_screen.h b/ext/native/ui/ui_screen.h index f2fe7b157f..fb0bda4494 100644 --- a/ext/native/ui/ui_screen.h +++ b/ext/native/ui/ui_screen.h @@ -237,11 +237,13 @@ private: class PopupMultiChoice : public UI::Choice { public: PopupMultiChoice(int *value, const std::string &text, const char **choices, int minVal, int numChoices, - const char *category, ScreenManager *screenManager, UI::LayoutParams *layoutParams = 0) + const char *category, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr) : UI::Choice(text, "", false, layoutParams), value_(value), choices_(choices), minVal_(minVal), numChoices_(numChoices), category_(category), screenManager_(screenManager) { - if (*value >= numChoices+minVal) *value = numChoices+minVal-1; - if (*value < minVal) *value = minVal; + if (*value >= numChoices + minVal) + *value = numChoices + minVal - 1; + if (*value < minVal) + *value = minVal; OnClick.Handle(this, &PopupMultiChoice::HandleClick); UpdateText(); } @@ -255,16 +257,19 @@ public: UI::Event OnChoice; -private: - void UpdateText(); - UI::EventReturn HandleClick(UI::EventParams &e); - - void ChoiceCallback(int num); - +protected: int *value_; const char **choices_; int minVal_; int numChoices_; + void UpdateText(); + +private: + UI::EventReturn HandleClick(UI::EventParams &e); + + void ChoiceCallback(int num); + virtual void PostChoiceCallback(int num) {} + const char *category_; ScreenManager *screenManager_; std::string valueText_; @@ -272,6 +277,40 @@ private: std::set hidden_; }; +// Allows passing in a dynamic vector of strings. Saves the string. +class PopupMultiChoiceDynamic : public PopupMultiChoice { +public: + PopupMultiChoiceDynamic(std::string *value, const std::string &text, std::vector choices, + const char *category, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr) + : UI::PopupMultiChoice(&valueInt_, text, nullptr, 0, (int)choices.size(), category, screenManager, layoutParams), + valueStr_(value) { + choices_ = new const char *[numChoices_]; + valueInt_ = 0; + for (int i = 0; i < numChoices_; i++) { + choices_[i] = new char[choices[i].size() + 1]; + memcpy((char *)choices_[i], choices[i].c_str(), choices[i].size() + 1); + if (*value == choices_[i]) + valueInt_ = i; + } + value_ = &valueInt_; + UpdateText(); + } + ~PopupMultiChoiceDynamic() { + for (int i = 0; i < numChoices_; i++) { + delete[] choices_[i]; + } + delete[] choices_; + } + +protected: + void PostChoiceCallback(int num) { + *valueStr_ = choices_[num]; + } + +private: + int valueInt_; + std::string *valueStr_; +}; class PopupSliderChoice : public Choice { public: From 8ee3cd52e89fab0d78d18fc2b99a5132712e25c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 15 Apr 2018 10:53:07 +0200 Subject: [PATCH 089/301] D3D11: Allow the user to select rendering device. --- Core/Config.cpp | 3 ++ Core/Config.h | 3 ++ UI/GameSettingsScreen.cpp | 8 +++-- Windows/GPU/D3D11Context.cpp | 53 +++++++++++++++++++++--------- Windows/GPU/D3D11Context.h | 5 ++- ext/native/thin3d/thin3d_create.h | 4 ++- ext/native/thin3d/thin3d_d3d11.cpp | 18 +++++----- 7 files changed, 65 insertions(+), 29 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 9350941f3c..04a7b488e6 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -508,6 +508,9 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true), ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend), ConfigSetting("VulkanDevice", &g_Config.VulkanDevice, "", true, false), +#ifdef _WIN32 + ConfigSetting("D3D11Device", &g_Config.D3D11Device, "", true, false), +#endif ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, &DefaultRenderingMode, true, true), ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true), ReportedConfigSetting("HardwareTransform", &g_Config.bHardwareTransform, true, true, true), diff --git a/Core/Config.h b/Core/Config.h index 3a022646c9..24f963edee 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -156,6 +156,9 @@ public: // We have separate device parameters for each backend so it doesn't get erased if you switch backends. // If not set, will use the "best" device. std::string VulkanDevice; +#ifdef _WIN32 + std::string D3D11Device; +#endif bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend bool bSoftwareSkinning; // may speed up some games diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index be1d8ef3e6..f41d8b4fdb 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -203,13 +203,17 @@ void GameSettingsScreen::CreateViews() { } #endif Draw::DrawContext *draw = screenManager()->getDrawContext(); - if (draw->GetDeviceList().size() > 0) { + if (draw->GetDeviceList().size() > 1) { // Some backends don't support switching so no point in showing multiple devices. std::string *deviceNameSetting = nullptr; if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { deviceNameSetting = &g_Config.VulkanDevice; } - +#ifdef _WIN32 + if (g_Config.iGPUBackend == (int)GPUBackend::DIRECT3D11) { + deviceNameSetting = &g_Config.D3D11Device; + } +#endif if (deviceNameSetting) { PopupMultiChoiceDynamic *deviceChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(deviceNameSetting, gr->T("Device"), draw->GetDeviceList(), nullptr, screenManager())); deviceChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend); diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 28b1540af3..4470464582 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -2,6 +2,7 @@ #include "Common/CommonWindows.h" #include +#include #include #include "base/logging.h" @@ -22,6 +23,12 @@ #define __uuidof(type) IID_##type #endif +#ifndef DXGI_ERROR_NOT_FOUND +#define _FACDXGI 0x87a +#define MAKE_DXGI_HRESULT(code) MAKE_HRESULT(1, _FACDXGI, code) +#define DXGI_ERROR_NOT_FOUND MAKE_DXGI_HRESULT(2) +#endif + #if PPSSPP_PLATFORM(UWP) #error This file should not be compiled for UWP. #endif @@ -41,7 +48,7 @@ void D3D11Context::SwapInterval(int interval) { // Dummy } -HRESULT D3D11Context::CreateTheDevice() { +HRESULT D3D11Context::CreateTheDevice(IDXGIAdapter *adapter) { bool windowed = true; #ifdef _DEBUG UINT createDeviceFlags = D3D11_CREATE_DEVICE_DEBUG; @@ -70,19 +77,13 @@ HRESULT D3D11Context::CreateTheDevice() { const UINT numFeatureLevels = ARRAYSIZE(featureLevels); HRESULT hr = S_OK; - // Temporarily commenting out until we can dynamically load D3D11CreateDevice. - for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++) { - driverType_ = driverTypes[driverTypeIndex]; - hr = ptr_D3D11CreateDevice(nullptr, driverType_, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)featureLevels, numFeatureLevels, + D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_UNKNOWN; + hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)featureLevels, numFeatureLevels, + D3D11_SDK_VERSION, &device_, &featureLevel_, &context_); + if (hr == E_INVALIDARG) { + // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it + hr = ptr_D3D11CreateDevice(adapter, driverType, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)&featureLevels[3], numFeatureLevels - 3, D3D11_SDK_VERSION, &device_, &featureLevel_, &context_); - - if (hr == E_INVALIDARG) { - // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it - hr = ptr_D3D11CreateDevice(nullptr, driverType_, nullptr, createDeviceFlags, (D3D_FEATURE_LEVEL *)&featureLevels[3], numFeatureLevels - 3, - D3D11_SDK_VERSION, &device_, &featureLevel_, &context_); - } - if (SUCCEEDED(hr)) - break; } return hr; } @@ -99,8 +100,30 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { LoadD3D11Error result = LoadD3D11(); HRESULT hr = E_FAIL; + std::vector adapterNames; if (result == LoadD3D11Error::SUCCESS) { - hr = CreateTheDevice(); + std::vector adapters; + int chosenAdapter = 0; + + IDXGIFactory * pFactory = nullptr; + ptr_CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pFactory); + + IDXGIAdapter *pAdapter; + for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; i++) { + adapters.push_back(pAdapter); + DXGI_ADAPTER_DESC desc; + pAdapter->GetDesc(&desc); + std::string str = ConvertWStringToUTF8(desc.Description); + adapterNames.push_back(str); + if (str == g_Config.D3D11Device) { + chosenAdapter = i; + } + } + + hr = CreateTheDevice(adapters[chosenAdapter]); + for (int i = 0; i < (int)adapters.size(); i++) { + adapters[i]->Release(); + } } if (FAILED(hr)) { @@ -146,7 +169,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { } #endif - draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, featureLevel_, hWnd_); + draw_ = Draw::T3DCreateD3D11Context(device_, context_, device1_, context1_, featureLevel_, hWnd_, adapterNames); SetGPUBackend(GPUBackend::DIRECT3D11); bool success = draw_->CreatePresets(); // If we can run D3D11, there's a compiler installed. I think. assert(success); diff --git a/Windows/GPU/D3D11Context.h b/Windows/GPU/D3D11Context.h index b0f4247628..88f1547295 100644 --- a/Windows/GPU/D3D11Context.h +++ b/Windows/GPU/D3D11Context.h @@ -40,7 +40,7 @@ public: Draw::DrawContext *GetDrawContext() override { return draw_; } private: - HRESULT CreateTheDevice(); + HRESULT CreateTheDevice(IDXGIAdapter *adapter); void LostBackbuffer(); void GotBackbuffer(); @@ -60,7 +60,6 @@ private: ID3D11InfoQueue *d3dInfoQueue_ = nullptr; #endif - D3D_DRIVER_TYPE driverType_; D3D_FEATURE_LEVEL featureLevel_ = D3D_FEATURE_LEVEL_11_0; int adapterId; HDC hDC; // Private GDI Device Context @@ -68,4 +67,4 @@ private: HMODULE hD3D11; int width; int height; -}; \ No newline at end of file +}; diff --git a/ext/native/thin3d/thin3d_create.h b/ext/native/thin3d/thin3d_create.h index 9d2ea4a844..a89c74ab96 100644 --- a/ext/native/thin3d/thin3d_create.h +++ b/ext/native/thin3d/thin3d_create.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include "thin3d/thin3d.h" // Separated this stuff into its own file so we don't get Windows.h included if all we want is the thin3d declarations. @@ -30,7 +32,7 @@ DrawContext *T3DCreateGLContext(); #ifdef _WIN32 DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx); -DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd); +DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector adapterNames); #endif DrawContext *T3DCreateVulkanContext(VulkanContext *context, bool splitSubmit); diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 9a3ce86e5a..f5e0c2da27 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -38,12 +38,15 @@ class D3D11RasterState; class D3D11DrawContext : public DrawContext { public: - D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd); + D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector deviceList); ~D3D11DrawContext(); const DeviceCaps &GetDeviceCaps() const override { return caps_; } + std::vector GetDeviceList() const override { + return deviceList_; + } uint32_t GetSupportedShaderLanguages() const override { return (uint32_t)ShaderLanguage::HLSL_D3D11 | (uint32_t)ShaderLanguage::HLSL_D3D11_BYTECODE; } @@ -210,15 +213,17 @@ private: // System info D3D_FEATURE_LEVEL featureLevel_; std::string adapterDesc_; + std::vector deviceList_; }; -D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd) +D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector deviceList) : device_(device), context_(deviceContext1), device1_(device1), context1_(deviceContext1), featureLevel_(featureLevel), - hWnd_(hWnd) { + hWnd_(hWnd), + deviceList_(deviceList) { // Seems like a fair approximation... caps_.dualSourceBlend = featureLevel_ >= D3D_FEATURE_LEVEL_10_0; @@ -229,7 +234,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.framebufferDepthBlitSupported = false; caps_.framebufferDepthCopySupported = true; - D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); if (SUCCEEDED(result)) { @@ -239,8 +243,6 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de // caps_.logicOpSupported = true; } } - // Obtain DXGI factory from device (since we used nullptr for pAdapter above) - IDXGIFactory1* dxgiFactory = nullptr; IDXGIDevice* dxgiDevice = nullptr; IDXGIAdapter* adapter = nullptr; HRESULT hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDevice)); @@ -1552,8 +1554,8 @@ void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h } } -DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd) { - return new D3D11DrawContext(device, context, device1, context1, featureLevel, hWnd); +DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector adapterNames) { + return new D3D11DrawContext(device, context, device1, context1, featureLevel, hWnd, adapterNames); } } // namespace Draw From 238521a2978e84c528e24f6015c218df117b0155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 6 Jun 2018 10:24:16 +0200 Subject: [PATCH 090/301] Rename device choice config options as requested. --- Core/Config.cpp | 4 ++-- Core/Config.h | 4 ++-- UI/GameSettingsScreen.cpp | 7 ++++--- Windows/GPU/D3D11Context.cpp | 2 +- Windows/GPU/WindowsVulkanContext.cpp | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 04a7b488e6..69ec5d6baa 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -507,9 +507,9 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("CardboardYShift", &g_Config.iCardboardXShift, 0, true, true), ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true), ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend), - ConfigSetting("VulkanDevice", &g_Config.VulkanDevice, "", true, false), + ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false), #ifdef _WIN32 - ConfigSetting("D3D11Device", &g_Config.D3D11Device, "", true, false), + ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), #endif ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, &DefaultRenderingMode, true, true), ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true), diff --git a/Core/Config.h b/Core/Config.h index 24f963edee..05b5f62c94 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -155,9 +155,9 @@ public: int iGPUBackend; // We have separate device parameters for each backend so it doesn't get erased if you switch backends. // If not set, will use the "best" device. - std::string VulkanDevice; + std::string sVulkanDevice; #ifdef _WIN32 - std::string D3D11Device; + std::string sD3D11Device; #endif bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f41d8b4fdb..cced4b40f3 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -203,15 +203,16 @@ void GameSettingsScreen::CreateViews() { } #endif Draw::DrawContext *draw = screenManager()->getDrawContext(); + + // Backends that don't allow a device choice will only expose one device. if (draw->GetDeviceList().size() > 1) { - // Some backends don't support switching so no point in showing multiple devices. std::string *deviceNameSetting = nullptr; if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { - deviceNameSetting = &g_Config.VulkanDevice; + deviceNameSetting = &g_Config.sVulkanDevice; } #ifdef _WIN32 if (g_Config.iGPUBackend == (int)GPUBackend::DIRECT3D11) { - deviceNameSetting = &g_Config.D3D11Device; + deviceNameSetting = &g_Config.sD3D11Device; } #endif if (deviceNameSetting) { diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 4470464582..7e6b748271 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -115,7 +115,7 @@ bool D3D11Context::Init(HINSTANCE hInst, HWND wnd, std::string *error_message) { pAdapter->GetDesc(&desc); std::string str = ConvertWStringToUTF8(desc.Description); adapterNames.push_back(str); - if (str == g_Config.D3D11Device) { + if (str == g_Config.sD3D11Device) { chosenAdapter = i; } } diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index 3ec3d08207..e82b5bc92b 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -109,10 +109,10 @@ bool WindowsVulkanContext::Init(HINSTANCE hInst, HWND hWnd, std::string *error_m g_Vulkan = nullptr; return false; } - int deviceNum = g_Vulkan->GetPhysicalDeviceByName(g_Config.VulkanDevice); + int deviceNum = g_Vulkan->GetPhysicalDeviceByName(g_Config.sVulkanDevice); if (deviceNum < 0) { deviceNum = g_Vulkan->GetBestPhysicalDevice(); - g_Config.VulkanDevice = g_Vulkan->GetPhysicalDeviceProperties(deviceNum).deviceName; + g_Config.sVulkanDevice = g_Vulkan->GetPhysicalDeviceProperties(deviceNum).deviceName; } g_Vulkan->ChooseDevice(deviceNum); if (g_Vulkan->EnableDeviceExtension(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME)) { From 0345750500259fd21d69c5cad8b195806834e9ee Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 5 Jun 2018 23:02:50 -0700 Subject: [PATCH 091/301] SDL: Fix EGL build. --- SDL/SDLGLGraphicsContext.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index 052c6e7ac8..e0cb354701 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -8,7 +8,9 @@ #include "Common/GraphicsContext.h" // TODO: Move this to a better place. -void EGL_Open(); +#if defined(USING_EGL) +int8_t EGL_Open(); +#endif class SDLGLGraphicsContext : public DummyGraphicsContext { public: From 61fc1cdcfb1abd75f3a5e992b62dc36c29fb19ec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Apr 2018 11:16:33 -0700 Subject: [PATCH 092/301] GPU: Dirty tex when clearing or rendering to self. This way if we're blitting, we'll re-blit. --- GPU/D3D11/StateMappingD3D11.cpp | 3 +++ GPU/Directx9/StateMappingDX9.cpp | 3 +++ GPU/GLES/DrawEngineGLES.cpp | 3 +++ GPU/Vulkan/DrawEngineVulkan.cpp | 3 +++ 4 files changed, 12 insertions(+) diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index ee76b37322..c1e669d268 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -426,6 +426,9 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + // This catches the case of clearing a texture. + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } } diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index b2e4595976..26390bef39 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -103,6 +103,9 @@ void DrawEngineDX9::ApplyDrawState(int prim) { if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + // This catches the case of clearing a texture. + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } // Start profiling here to skip SetTexture which is already accounted for diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index d79ee4efd4..29eb3ad9d4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -311,6 +311,9 @@ void DrawEngineGLES::DoFlush() { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); textureNeedsApply = true; + } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + // This catches the case of clearing a texture. + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } GEPrimitiveType prim = prevPrim_; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 76643d6066..b47d23f6a3 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -570,6 +570,9 @@ void DrawEngineVulkan::DoFlush() { textureCache_->SetTexture(); gstate_c.Clean(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); textureNeedsApply = true; + } else if (gstate.getTextureAddress(0) == ((gstate.getFrameBufRawAddress() | 0x04000000) & 0x3FFFFFFF)) { + // This catches the case of clearing a texture. + gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } GEPrimitiveType prim = prevPrim_; From b845278d7d8048d7fce1ef0b7bc1ec4a04f6a684 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Apr 2018 16:18:35 -0700 Subject: [PATCH 093/301] D3D11: Implement flat shading in shader. --- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 3 ++- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index 73bd6165e4..7539dfa4e9 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -135,7 +135,8 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag if (doTexture) { WRITE(p, " float3 v_texcoord: TEXCOORD0;\n"); } - WRITE(p, " float4 v_color0: COLOR0;\n"); + const char *colorInterpolation = doFlatShading && lang == HLSL_D3D11 ? "nointerpolation " : ""; + WRITE(p, " %sfloat4 v_color0: COLOR0;\n", colorInterpolation); if (lmode) { WRITE(p, " float3 v_color1: COLOR1;\n"); } diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 7b1b542558..227c51223c 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -231,7 +231,8 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage if (doTexture) { WRITE(p, " float3 v_texcoord : TEXCOORD0;\n"); } - WRITE(p, " float4 v_color0 : COLOR0;\n"); + const char *colorInterpolation = doFlatShading && lang == HLSL_D3D11 ? "nointerpolation " : ""; + WRITE(p, " %sfloat4 v_color0 : COLOR0;\n", colorInterpolation); if (lmode) WRITE(p, " float3 v_color1 : COLOR1;\n"); From 5b62c8afb80a3aedb05dcbe2c50e48bedbbd87c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Apr 2018 16:31:39 -0700 Subject: [PATCH 094/301] D3D9: Implement flat shading via state. --- GPU/Directx9/StateMappingDX9.cpp | 3 +++ GPU/GPUCommon.cpp | 3 ++- ext/native/gfx/d3d9_state.cpp | 1 + ext/native/gfx/d3d9_state.h | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index b2e4595976..bd580b350e 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -204,10 +204,13 @@ void DrawEngineDX9::ApplyDrawState(int prim) { if (gstate.isModeClear()) { // Set Cull dxstate.cullMode.set(false, false); + // Well, probably doesn't matter... + dxstate.shadeMode.set(D3DSHADE_GOURAUD); } else { // Set cull bool wantCull = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled(); dxstate.cullMode.set(wantCull, gstate.getCullMode()); + dxstate.shadeMode.set(gstate.getShadeMode() == GE_SHADE_GOURAUD ? D3DSHADE_GOURAUD : D3DSHADE_FLAT); } } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 262ed460a7..1950eab058 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -72,7 +72,8 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_FOGENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE}, { GE_CMD_TEXMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE }, { GE_CMD_TEXSHADELS, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE }, - { GE_CMD_SHADEMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE }, + // Raster state for Direct3D 9, uncommon. + { GE_CMD_SHADEMODE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_RASTER_STATE }, { GE_CMD_TEXFUNC, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE }, { GE_CMD_COLORTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE }, { GE_CMD_ALPHATESTENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAGMENTSHADER_STATE }, diff --git a/ext/native/gfx/d3d9_state.cpp b/ext/native/gfx/d3d9_state.cpp index aa20d88151..fe7849b894 100644 --- a/ext/native/gfx/d3d9_state.cpp +++ b/ext/native/gfx/d3d9_state.cpp @@ -35,6 +35,7 @@ void DirectXState::Restore() { scissorRect.restore(); count++; cullMode.restore(); count++; + shadeMode.restore(); count++; depthTest.restore(); count++; depthFunc.restore(); count++; diff --git a/ext/native/gfx/d3d9_state.h b/ext/native/gfx/d3d9_state.h index a9a8eed86d..3304c22a1e 100644 --- a/ext/native/gfx/d3d9_state.h +++ b/ext/native/gfx/d3d9_state.h @@ -455,6 +455,7 @@ public: BoolUnused dither; CullMode cullMode; + DxState1 shadeMode; BoolState depthTest; From dca159c47e676190f33c21b886c5e8057dff5f88 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Apr 2018 16:32:09 -0700 Subject: [PATCH 095/301] GPU: Correct sw flat shading color on Vulkan/D3D. It was only correct on GLES before. This only fixes software transform. --- GPU/Common/SoftwareTransformCommon.cpp | 48 ++++++++++++++++++++++++-- GPU/Common/SoftwareTransformCommon.h | 1 + GPU/D3D11/DrawEngineD3D11.cpp | 1 + GPU/Directx9/DrawEngineDX9.cpp | 1 + GPU/GLES/DrawEngineGLES.cpp | 1 + GPU/Vulkan/DrawEngineVulkan.cpp | 1 + 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index a29c987c11..93385d8aa3 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -125,6 +125,33 @@ static bool IsReallyAClear(const TransformedVertex *transformed, int numVerts, f return true; } +static int ColorIndexOffset(int prim, GEShadeMode shadeMode, bool clearMode) { + if (shadeMode != GE_SHADE_FLAT || clearMode) { + return 0; + } + + switch (prim) { + case GE_PRIM_LINES: + case GE_PRIM_LINE_STRIP: + return 1; + + case GE_PRIM_TRIANGLES: + case GE_PRIM_TRIANGLE_STRIP: + return 2; + + case GE_PRIM_TRIANGLE_FAN: + return 1; + + case GE_PRIM_RECTANGLES: + // We already use BR color when expanding, so no need to offset. + return 0; + + default: + break; + } + return 0; +} + void SoftwareTransform( int prim, int vertexCount, u32 vertType, u16 *&inds, int indexType, const DecVtxFormat &decVtxFormat, int &maxIndex, TransformedVertex *&drawBuffer, int &numTrans, bool &drawIndexed, const SoftwareTransformParams *params, SoftwareTransformResult *result) { @@ -168,6 +195,11 @@ void SoftwareTransform( fog_slope = 1.0f; } + int colorIndOffset = 0; + if (params->provokeFlatFirst) { + colorIndOffset = ColorIndexOffset(prim, gstate.getShadeMode(), gstate.isModeClear()); + } + VertexReader reader(decoded, decVtxFormat, vertType); if (throughmode) { for (int index = 0; index < maxIndex; index++) { @@ -178,7 +210,13 @@ void SoftwareTransform( reader.ReadPos(vert.pos); if (reader.hasColor0()) { - reader.ReadColor0_8888(vert.color0); + if (colorIndOffset != 0 && index + colorIndOffset < maxIndex) { + reader.Goto(index + colorIndOffset); + reader.ReadColor0_8888(vert.color0); + reader.Goto(index); + } else { + reader.ReadColor0_8888(vert.color0); + } } else { vert.color0_32 = gstate.getMaterialAmbientRGBA(); } @@ -260,7 +298,13 @@ void SoftwareTransform( // Perform lighting here if enabled. don't need to check through, it's checked above. Vec4f unlitColor = Vec4f(1, 1, 1, 1); if (reader.hasColor0()) { - reader.ReadColor0(&unlitColor.x); + if (colorIndOffset != 0 && index + colorIndOffset < maxIndex) { + reader.Goto(index + colorIndOffset); + reader.ReadColor0(&unlitColor.x); + reader.Goto(index); + } else { + reader.ReadColor0(&unlitColor.x); + } } else { unlitColor = Vec4f::FromRGBA(gstate.getMaterialAmbientRGBA()); } diff --git a/GPU/Common/SoftwareTransformCommon.h b/GPU/Common/SoftwareTransformCommon.h index 9bc2e4da49..a7efc396c9 100644 --- a/GPU/Common/SoftwareTransformCommon.h +++ b/GPU/Common/SoftwareTransformCommon.h @@ -46,6 +46,7 @@ struct SoftwareTransformParams { TextureCacheCommon *texCache; bool allowClear; bool allowSeparateAlphaClear; + bool provokeFlatFirst; }; void SoftwareTransform(int prim, int vertexCount, u32 vertexType, u16 *&inds, int indexType, const DecVtxFormat &decVtxFormat, int &maxIndex, TransformedVertex *&drawBuffer, diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 2b49ae2d6e..4651c38290 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -587,6 +587,7 @@ rotateVBO: params.texCache = textureCache_; params.allowClear = true; params.allowSeparateAlphaClear = false; // D3D11 doesn't support separate alpha clears + params.provokeFlatFirst = true; int maxIndex = indexGen.MaxIndex(); SoftwareTransform( diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index c72d256727..de1a85d093 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -545,6 +545,7 @@ rotateVBO: params.texCache = textureCache_; params.allowClear = true; params.allowSeparateAlphaClear = true; + params.provokeFlatFirst = true; int maxIndex = indexGen.MaxIndex(); SoftwareTransform( diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index d79ee4efd4..79589a113b 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -553,6 +553,7 @@ rotateVBO: params.texCache = textureCache_; params.allowClear = true; params.allowSeparateAlphaClear = true; + params.provokeFlatFirst = false; int maxIndex = indexGen.MaxIndex(); int vertexCount = indexGen.VertexCount(); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 76643d6066..f68f989e7f 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -860,6 +860,7 @@ void DrawEngineVulkan::DoFlush() { // do not respect scissor rects. params.allowClear = g_Config.iRenderingMode != 0; params.allowSeparateAlphaClear = false; + params.provokeFlatFirst = true; int maxIndex = indexGen.MaxIndex(); SoftwareTransform( From 2606365ba4d74ca77597a4a33096fbcdf115992c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Apr 2018 16:38:29 -0700 Subject: [PATCH 096/301] GPU: Use software transform for flat shading. Except on GLES where it works fine with the default provoking index. This fixes #10969 so that it works everywhere. --- GPU/D3D11/DrawEngineD3D11.cpp | 3 ++- GPU/Directx9/ShaderManagerDX9.cpp | 3 ++- GPU/Vulkan/DrawEngineVulkan.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 4651c38290..2e09aee228 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -331,7 +331,8 @@ void DrawEngineD3D11::DoFlush() { GEPrimitiveType prim = prevPrim_; ApplyDrawState(prim); - bool useHWTransform = CanUseHardwareTransform(prim); + // Always use software for flat shading to fix the provoking index. + bool useHWTransform = CanUseHardwareTransform(prim) && gstate.getShadeMode() != GE_SHADE_FLAT; if (useHWTransform) { ID3D11Buffer *vb_ = nullptr; diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 70d4cb7693..f910f6cc8f 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -537,7 +537,8 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays } VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { - bool useHWTransform = CanUseHardwareTransform(prim); + // Always use software for flat shading to fix the provoking index. + bool useHWTransform = CanUseHardwareTransform(prim) && gstate.getShadeMode() != GE_SHADE_FLAT; VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index f68f989e7f..2722ade2df 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -574,7 +574,8 @@ void DrawEngineVulkan::DoFlush() { GEPrimitiveType prim = prevPrim_; - bool useHWTransform = CanUseHardwareTransform(prim); + // Always use software for flat shading to fix the provoking index. + bool useHWTransform = CanUseHardwareTransform(prim) && gstate.getShadeMode() != GE_SHADE_FLAT; VulkanVertexShader *vshader = nullptr; VulkanFragmentShader *fshader = nullptr; From e69e8446237f31b1518838456460fe98000cfe7b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 28 May 2018 17:08:55 -0700 Subject: [PATCH 097/301] Ctrl: Always mark non-user buttons released. This matches behavior on real firmware and fixes a test failure. --- Core/HLE/sceCtrl.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 1ef00dbdaf..1b552a4191 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -503,18 +503,20 @@ static int sceCtrlPeekBufferNegative(u32 ctrlDataPtr, u32 nBufs) return done; } -void __CtrlWriteUserLatch(CtrlLatch *userLatch) { +static void __CtrlWriteUserLatch(CtrlLatch *userLatch, int bufs) { *userLatch = latch; userLatch->btnBreak &= CTRL_MASK_USER; userLatch->btnMake &= CTRL_MASK_USER; userLatch->btnPress &= CTRL_MASK_USER; - userLatch->btnRelease &= CTRL_MASK_USER; + if (bufs > 0) { + userLatch->btnRelease |= CTRL_MASK_USER; + } } static u32 sceCtrlPeekLatch(u32 latchDataPtr) { auto userLatch = PSPPointer::Create(latchDataPtr); if (userLatch.IsValid()) { - __CtrlWriteUserLatch(userLatch); + __CtrlWriteUserLatch(userLatch, ctrlLatchBufs); } return hleLogSuccessI(SCECTRL, ctrlLatchBufs); } @@ -522,7 +524,7 @@ static u32 sceCtrlPeekLatch(u32 latchDataPtr) { static u32 sceCtrlReadLatch(u32 latchDataPtr) { auto userLatch = PSPPointer::Create(latchDataPtr); if (userLatch.IsValid()) { - __CtrlWriteUserLatch(userLatch); + __CtrlWriteUserLatch(userLatch, ctrlLatchBufs); } return hleLogSuccessI(SCECTRL, __CtrlResetLatch()); } From 16e0a7cc8e9f49a102df2b202ef64447a2f4f29e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 28 May 2018 17:24:40 -0700 Subject: [PATCH 098/301] Headless: Default memory stick inserted. Tests expect this, oops. --- headless/Headless.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 661211d1d0..d81a229d16 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -374,6 +374,7 @@ int main(int argc, const char* argv[]) g_Config.bBlockTransferGPU = true; g_Config.iSplineBezierQuality = 2; g_Config.bHighQualityDepth = true; + g_Config.bMemStickInserted = true; #ifdef _WIN32 InitSysDirectories(); From 890dfa46827245cdaea9089d2c334e65ef842336 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 12:08:51 -0700 Subject: [PATCH 099/301] UI: Show compat rating radios next to each other. When there's space, let's show them horizontally. It's easier to see them all. See #11129. --- UI/ReportScreen.cpp | 20 ++++++++++++++++---- UI/ReportScreen.h | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 549a86f604..f1c00a5e21 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -170,6 +170,11 @@ void ReportScreen::update() { UIDialogScreenWithGameBackground::update(); } +void ReportScreen::resized() { + UIDialogScreenWithGameBackground::resized(); + RecreateViews(); +} + EventReturn ReportScreen::HandleChoice(EventParams &e) { if (overall_ == ReportingOverallScore::NONE) { graphics_ = 0; @@ -218,6 +223,7 @@ void ReportScreen::CreateViews() { Margins actionMenuMargins(0, 20, 15, 0); Margins contentMargins(0, 20, 5, 5); + float leftColumnWidth = dp_xres - actionMenuMargins.horiz() - contentMargins.horiz() - 300.0f; ViewGroup *leftColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 0.4f, contentMargins)); LinearLayout *leftColumnItems = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(WRAP_CONTENT, FILL_PARENT)); ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); @@ -259,9 +265,13 @@ void ReportScreen::CreateViews() { leftColumnItems->Add(new CompatRatingChoice("Overall", (int *)&overall_))->SetEnabledPtr(&enableReporting_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); overallDescription_ = leftColumnItems->Add(new TextView("", new LinearLayoutParams(Margins(10, 0)))); overallDescription_->SetShadow(true); - leftColumnItems->Add(new RatingChoice("Graphics", &graphics_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); - leftColumnItems->Add(new RatingChoice("Speed", &speed_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); - leftColumnItems->Add(new RatingChoice("Gameplay", &gameplay_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); + + UI::Orientation ratingsOrient = leftColumnWidth >= 750.0f ? ORIENT_HORIZONTAL : ORIENT_VERTICAL; + UI::LinearLayout *ratingsHolder = new LinearLayout(ratingsOrient, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + leftColumnItems->Add(ratingsHolder); + ratingsHolder->Add(new RatingChoice("Graphics", &graphics_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); + ratingsHolder->Add(new RatingChoice("Speed", &speed_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); + ratingsHolder->Add(new RatingChoice("Gameplay", &gameplay_))->SetEnabledPtr(&ratingEnabled_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); rightColumnItems->SetSpacing(0.0f); rightColumnItems->Add(new Choice(rp->T("Open Browser")))->OnClick.Handle(this, &ReportScreen::HandleBrowser); @@ -288,16 +298,18 @@ void ReportScreen::UpdateSubmit() { void ReportScreen::UpdateOverallDescription() { I18NCategory *rp = GetI18NCategory("Reporting"); const char *desc; + uint32_t c = 0xFFFFFFFF; switch (overall_) { case ReportingOverallScore::PERFECT: desc = rp->T("Perfect Description", "Flawless emulation for the entire game - great!"); break; case ReportingOverallScore::PLAYABLE: desc = rp->T("Plays Description", "Fully playable but might be with glitches"); break; case ReportingOverallScore::INGAME: desc = rp->T("In-game Description", "Gets into gameplay, but too buggy too complete"); break; case ReportingOverallScore::MENU: desc = rp->T("Menu/Intro Description", "Can't get into the game itself"); break; - case ReportingOverallScore::NONE: desc = rp->T("Nothing Description", "Completely broken"); break; + case ReportingOverallScore::NONE: desc = rp->T("Nothing Description", "Completely broken"); c = 0xFF0000FF; break; default: desc = rp->T("Unselected Overall Description", "How well does this game emulate?"); break; } overallDescription_->SetText(desc); + overallDescription_->SetTextColor(c); } EventReturn ReportScreen::HandleSubmit(EventParams &e) { diff --git a/UI/ReportScreen.h b/UI/ReportScreen.h index f43b2e9b19..40eed81b67 100644 --- a/UI/ReportScreen.h +++ b/UI/ReportScreen.h @@ -38,6 +38,7 @@ public: protected: void update() override; + void resized() override; void CreateViews() override; void UpdateSubmit(); void UpdateOverallDescription(); From 4cbfb4aa98f05cda39f5287ed0b47de03c1847c8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 14:47:49 -0700 Subject: [PATCH 100/301] Reporting: Provide suggestions on negative reports. Based on other reports, try suggesting settings to change or actions to take. Fairly rudimentary currently. --- Core/Reporting.cpp | 19 ++++++++--- Core/Reporting.h | 4 +++ UI/ReportScreen.cpp | 82 +++++++++++++++++++++++++++++++++++++++------ UI/ReportScreen.h | 9 +++-- 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 02503301d8..a927c55510 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -67,6 +67,8 @@ namespace Reporting static bool currentSupported = false; // Whether the most recent server request seemed successful. static bool serverWorking = true; + // The latest compatibility result from the server. + static std::vector lastCompatResult; enum class RequestType { @@ -423,6 +425,7 @@ namespace Reporting postdata.Add("speed", StringFromFormat("%d", payload.int2)); postdata.Add("gameplay", StringFromFormat("%d", payload.int3)); postdata.Add("crc", StringFromFormat("%08x", Core_GetPowerSaving() ? 0 : RetrieveCRC())); + postdata.Add("suggestions", payload.string1 != "perfect" && payload.string1 != "playable" ? "1" : "0"); AddScreenshotData(postdata, payload.string2); payload.string1.clear(); payload.string2.clear(); @@ -432,12 +435,14 @@ namespace Reporting if (!SendReportRequest("/report/compat", postdata.ToString(), postdata.GetMimeType(), &output)) { serverWorking = false; } else { - char res = 0; - if (!output.empty()) { - output.Take(1, &res); - } - if (res == 0) + std::string result; + output.TakeAll(&result); + + lastCompatResult.clear(); + if (result.empty() || result[0] == '0') serverWorking = false; + else if (result[0] != '1') + SplitString(result, '\n', lastCompatResult); } break; @@ -594,4 +599,8 @@ namespace Reporting std::thread th(Process, pos); th.detach(); } + + std::vector CompatibilitySuggestions() { + return lastCompatResult; + } } diff --git a/Core/Reporting.h b/Core/Reporting.h index 08acc9d267..c34f815119 100644 --- a/Core/Reporting.h +++ b/Core/Reporting.h @@ -20,6 +20,7 @@ #include "Common/CommonTypes.h" #include "Common/Log.h" #include +#include #define DEBUG_LOG_REPORT(t,...) do { DEBUG_LOG(t, __VA_ARGS__); Reporting::ReportMessage(__VA_ARGS__); } while (false) #define ERROR_LOG_REPORT(t,...) do { ERROR_LOG(t, __VA_ARGS__); Reporting::ReportMessage(__VA_ARGS__); } while (false) @@ -74,6 +75,9 @@ namespace Reporting // Report the compatibility of the current game / configuration. void ReportCompatibility(const char *compat, int graphics, int speed, int gameplay, const std::string &screenshotFilename); + // Get the latest compatibility result. Only valid when GetStatus() is not BUSY. + std::vector CompatibilitySuggestions(); + // Returns true if that identifier has not been logged yet. bool ShouldLogOnce(const char *identifier); diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index f1c00a5e21..70c92cf26d 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -17,6 +17,8 @@ #include #include "base/display.h" +// TODO: For text align flags, probably shouldn';t be in gfx_es2/... +#include "gfx_es2/draw_buffer.h" #include "i18n/i18n.h" #include "thin3d/thin3d.h" #include "ui/ui_context.h" @@ -66,7 +68,7 @@ RatingChoice::RatingChoice(const char *captionKey, int *value, LayoutParams *lay I18NCategory *rp = GetI18NCategory("Reporting"); group_ = new LinearLayout(ORIENT_HORIZONTAL); - Add(new TextView(rp->T(captionKey)))->SetShadow(true); + Add(new TextView(rp->T(captionKey), FLAG_WRAP_TEXT, false))->SetShadow(true); Add(group_); group_->SetSpacing(0.0f); @@ -229,9 +231,9 @@ void ReportScreen::CreateViews() { ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL); - leftColumnItems->Add(new TextView(rp->T("FeedbackDesc", "How's the emulation? Let us and the community know!"), new LinearLayoutParams(Margins(12, 5, 0, 5))))->SetShadow(true); + leftColumnItems->Add(new TextView(rp->T("FeedbackDesc", "How's the emulation? Let us and the community know!"), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5))))->SetShadow(true); if (!Reporting::IsEnabled()) { - reportingNotice_ = leftColumnItems->Add(new TextView(rp->T("FeedbackDisabled", "Compatibility server reports must be enabled."), new LinearLayoutParams(Margins(12, 5, 0, 5)))); + reportingNotice_ = leftColumnItems->Add(new TextView(rp->T("FeedbackDisabled", "Compatibility server reports must be enabled."), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5)))); reportingNotice_->SetShadow(true); reportingNotice_->SetTextColor(0xFF3030FF); CheckBox *reporting = leftColumnItems->Add(new CheckBox(&enableReporting_, sy->T("Enable Compatibility Server Reports"))); @@ -243,7 +245,10 @@ void ReportScreen::CreateViews() { #ifdef MOBILE_DEVICE if (!Core_GetPowerSaving()) { - leftColumnItems->Add(new TextView(rp->T("FeedbackIncludeCRC", "Note: Battery will be used to send a disc CRC"), new LinearLayoutParams(Margins(12, 5, 0, 5))))->SetEnabledPtr(&enableReporting_); + auto crcWarning = new TextView(rp->T("FeedbackIncludeCRC", "Note: Battery will be used to send a disc CRC"), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5))); + crcWarning->SetShadow(true); + crcWarning->SetEnabledPtr(&enableReporting_); + leftColumnItems->Add(crcWarning); } #endif @@ -263,7 +268,7 @@ void ReportScreen::CreateViews() { } leftColumnItems->Add(new CompatRatingChoice("Overall", (int *)&overall_))->SetEnabledPtr(&enableReporting_)->OnChoice.Handle(this, &ReportScreen::HandleChoice); - overallDescription_ = leftColumnItems->Add(new TextView("", new LinearLayoutParams(Margins(10, 0)))); + overallDescription_ = leftColumnItems->Add(new TextView("", FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(10, 0)))); overallDescription_->SetShadow(true); UI::Orientation ratingsOrient = leftColumnWidth >= 750.0f ? ORIENT_HORIZONTAL : ORIENT_VERTICAL; @@ -331,7 +336,7 @@ EventReturn ReportScreen::HandleSubmit(EventParams &e) { std::string filename = includeScreenshot_ ? screenshotFilename_ : ""; Reporting::ReportCompatibility(compat, graphics_ + 1, speed_ + 1, gameplay_ + 1, filename); TriggerFinish(DR_OK); - screenManager()->push(new ReportFinishScreen(gamePath_)); + screenManager()->push(new ReportFinishScreen(gamePath_, overall_)); return EVENT_DONE; } @@ -341,8 +346,8 @@ EventReturn ReportScreen::HandleBrowser(EventParams &e) { return EVENT_DONE; } -ReportFinishScreen::ReportFinishScreen(const std::string &gamePath) - : UIDialogScreenWithGameBackground(gamePath), resultNotice_(nullptr), setStatus_(false) { +ReportFinishScreen::ReportFinishScreen(const std::string &gamePath, ReportingOverallScore score) + : UIDialogScreenWithGameBackground(gamePath), score_(score) { } void ReportFinishScreen::CreateViews() { @@ -356,8 +361,15 @@ void ReportFinishScreen::CreateViews() { ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL); - leftColumnItems->Add(new TextView(rp->T("FeedbackThanks", "Thanks for your feedback."), new LinearLayoutParams(Margins(12, 5, 0, 5)))); - resultNotice_ = leftColumnItems->Add(new TextView(rp->T("FeedbackDelayInfo", "Your data is being submitted in the background."), new LinearLayoutParams(Margins(12, 5, 0, 5)))); + leftColumnItems->Add(new TextView(rp->T("FeedbackThanks", "Thanks for your feedback."), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5))))->SetShadow(true); + if (score_ == ReportingOverallScore::PERFECT || score_ == ReportingOverallScore::PLAYABLE) { + resultNotice_ = leftColumnItems->Add(new TextView(rp->T("FeedbackDelayInfo", "Your data is being submitted in the background."), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5)))); + } else { + resultNotice_ = leftColumnItems->Add(new TextView(rp->T("SuggestionsWaiting", "Submitting and checking other user feedback.."), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5)))); + } + resultNotice_->SetShadow(true); + resultItems_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(12, 5, 0, 5))); + leftColumnItems->Add(resultItems_); rightColumnItems->SetSpacing(0.0f); rightColumnItems->Add(new Choice(rp->T("View Feedback")))->OnClick.Handle(this, &ReportFinishScreen::HandleViewFeedback); @@ -380,11 +392,13 @@ void ReportFinishScreen::update() { Reporting::ReportStatus status = Reporting::GetStatus(); switch (status) { case Reporting::ReportStatus::WORKING: - resultNotice_->SetText(rp->T("FeedbackSubmitDone", "Your data has been submitted.")); + ShowSuggestions(); + setStatus_ = true; break; case Reporting::ReportStatus::FAILING: resultNotice_->SetText(rp->T("FeedbackSubmitFail", "Could not submit data to server. Try updating PPSSPP.")); + setStatus_ = true; break; case Reporting::ReportStatus::BUSY: @@ -397,6 +411,52 @@ void ReportFinishScreen::update() { UIDialogScreenWithGameBackground::update(); } +void ReportFinishScreen::ShowSuggestions() { + I18NCategory *rp = GetI18NCategory("Reporting"); + + auto suggestions = Reporting::CompatibilitySuggestions(); + if (score_ == ReportingOverallScore::PERFECT || score_ == ReportingOverallScore::PLAYABLE) { + resultNotice_->SetText(rp->T("FeedbackSubmitDone", "Your data has been submitted.")); + } else if (suggestions.empty()) { + resultNotice_->SetText(rp->T("SuggestionsNone", "This game isn't working for other users too.")); + } else { + resultNotice_->SetText(rp->T("SuggestionsFound", "Other users have reported better results. Tap View Feedback for more detail.")); + + resultItems_->Clear(); + bool shownConfig = false; + bool valid = false; + for (auto item : suggestions) { + const char *suggestion = nullptr; + if (item == "Upgrade") { + suggestion = rp->T("SuggestionUpgrade", "Upgrade to a newer PPSSPP build"); + } if (item == "Downgrade") { + suggestion = rp->T("SuggestionDowngrade", "Downgrade to an older PPSSPP version (please report this bug)"); + } else if (item == "VerifyDisc") { + suggestion = rp->T("SuggestionVerifyDisc", "Check your ISO is a good copy of your disc"); + } else if (item == "Config:CPUSpeed:0") { + suggestion = rp->T("SuggestionCPUSpeed0", "Disable locked CPU speed setting"); + } else { + bool isConfig = startsWith(item, "Config:"); + if (isConfig && !shownConfig) { + suggestion = rp->T("SuggestionConfig", "See reports on website for good settings"); + shownConfig = true; + } + // Ignore unknown configs, hopefully we recognized "Upgrade" at least. + } + + if (suggestion) { + valid = true; + resultItems_->Add(new TextView(std::string(" - ") + suggestion, FLAG_WRAP_TEXT, false))->SetShadow(true); + } + } + + if (!valid) { + // No actual valid versions. Let's just say upgrade and hope the server's not broken. + resultItems_->Add(new TextView(std::string(" - ") + rp->T("SuggestionUpgrade", "Upgrade to a newer PPSSPP build"), FLAG_WRAP_TEXT, false))->SetShadow(true); + } + } +} + UI::EventReturn ReportFinishScreen::HandleViewFeedback(UI::EventParams &e) { const std::string url = "http://" + Reporting::ServerHost() + "/game/" + Reporting::CurrentGameID(); LaunchBrowser(url.c_str()); diff --git a/UI/ReportScreen.h b/UI/ReportScreen.h index 40eed81b67..18c0d2b930 100644 --- a/UI/ReportScreen.h +++ b/UI/ReportScreen.h @@ -65,14 +65,17 @@ protected: class ReportFinishScreen : public UIDialogScreenWithGameBackground { public: - ReportFinishScreen(const std::string &gamePath); + ReportFinishScreen(const std::string &gamePath, ReportingOverallScore score); protected: void update() override; void CreateViews() override; + void ShowSuggestions(); UI::EventReturn HandleViewFeedback(UI::EventParams &e); - UI::TextView *resultNotice_; - bool setStatus_; + UI::TextView *resultNotice_ = nullptr; + UI::LinearLayout *resultItems_ = nullptr; + ReportingOverallScore score_; + bool setStatus_ = false; }; From cb0838f86f3b119f29d7e33bb7eb786df5fd09ab Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 14:50:20 -0700 Subject: [PATCH 101/301] Reporting: Fix typo. --- UI/ReportScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 70c92cf26d..8d8fb86100 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -307,7 +307,7 @@ void ReportScreen::UpdateOverallDescription() { switch (overall_) { case ReportingOverallScore::PERFECT: desc = rp->T("Perfect Description", "Flawless emulation for the entire game - great!"); break; case ReportingOverallScore::PLAYABLE: desc = rp->T("Plays Description", "Fully playable but might be with glitches"); break; - case ReportingOverallScore::INGAME: desc = rp->T("In-game Description", "Gets into gameplay, but too buggy too complete"); break; + case ReportingOverallScore::INGAME: desc = rp->T("In-game Description", "Gets into gameplay, but too buggy to complete"); break; case ReportingOverallScore::MENU: desc = rp->T("Menu/Intro Description", "Can't get into the game itself"); break; case ReportingOverallScore::NONE: desc = rp->T("Nothing Description", "Completely broken"); c = 0xFF0000FF; break; default: desc = rp->T("Unselected Overall Description", "How well does this game emulate?"); break; From 7c8780bfb6a50e876e231d0f72f948743e88e87d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 07:41:00 -0700 Subject: [PATCH 102/301] GPU: Remove some unused code. --- GPU/GLES/FramebufferManagerGLES.cpp | 57 ----------------------------- GPU/Vulkan/FramebufferVulkan.cpp | 53 --------------------------- GPU/Vulkan/FramebufferVulkan.h | 16 -------- 3 files changed, 126 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 5c357ac90e..d27353898d 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -78,8 +78,6 @@ static const char basic_vs[] = const int MAX_PBO = 2; -void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format); - void FramebufferManagerGLES::CompileDraw2DProgram() { if (!draw2dprogram_) { std::string errorString; @@ -679,61 +677,6 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); } -void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - // Here let's assume they don't intersect - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_5551: // ABGR 1555 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_4444: // ABGR 4444 - { - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } - } -} - void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { if (!vfb->fbo) { ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0"); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index b72d35cbf9..171a1205a8 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -80,8 +80,6 @@ void main() { } )"; -void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format); - FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) : FramebufferManagerCommon(draw), vulkan_(vulkan), @@ -535,57 +533,6 @@ void FramebufferManagerVulkan::BlitFramebuffer(VirtualFramebuffer *dst, int dstX } } -// TODO: SSE/NEON -// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :) -void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) { - // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. - const u32 *src32 = (const u32 *)src; - - if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - // Here let's assume they don't intersect - for (u32 y = 0; y < height; ++y) { - memcpy(dst32, src32, width * 4); - src32 += srcStride; - dst32 += dstStride; - } - } - } else { - // But here it shouldn't matter if they do intersect - u16 *dst16 = (u16 *)dst; - switch (format) { - case GE_FORMAT_565: // BGR 565 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGB565(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_5551: // ABGR 1555 - for (u32 y = 0; y < height; ++y) { - ConvertBGRA8888ToRGBA5551(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_4444: // ABGR 4444 - for (u32 y = 0; y < height; ++y) { - ConvertRGBA8888ToRGBA4444(dst16, src32, width); - src32 += srcStride; - dst16 += dstStride; - } - break; - case GE_FORMAT_8888: - case GE_FORMAT_INVALID: - // Not possible. - break; - } - } -} - void FramebufferManagerVulkan::BeginFrameVulkan() { BeginFrame(); } diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 197802e4bc..e55fb0f1eb 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -24,14 +24,6 @@ #include "GPU/Vulkan/VulkanUtil.h" #include "GPU/Vulkan/DepalettizeShaderVulkan.h" -// TODO: Remove? -enum VulkanFBOColorDepth { - VK_FBO_8888, - VK_FBO_565, - VK_FBO_4444, - VK_FBO_5551, -}; - class TextureCacheVulkan; class DrawEngineVulkan; class VulkanContext; @@ -39,14 +31,6 @@ class ShaderManagerVulkan; class VulkanTexture; class VulkanPushBuffer; -static const char *ub_post_shader = -R"( vec2 texelDelta; - vec2 pixelDelta; - vec4 time; -)"; - -class VulkanPushBuffer; - class FramebufferManagerVulkan : public FramebufferManagerCommon { public: FramebufferManagerVulkan(Draw::DrawContext *draw, VulkanContext *vulkan); From e90b01d9acca4792ecb11f022df023edbb95475b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 07:41:25 -0700 Subject: [PATCH 103/301] thin3d: Convert BGRA8888 to RGB888 for screenshots. In case we prefer BGRA for some buffer in Vulkan. Also reporting. --- ext/native/thin3d/thin3d.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 965c88dc0b..72afd1cfe3 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -428,12 +428,22 @@ void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u src32 += srcStride; dst32 += dstStride; } - } - else { - // Don't even bother with these, this path only happens in screenshots and we don't save those to 16-bit. - assert(false); + } else if (format == Draw::DataFormat::R8G8B8_UNORM) { + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + uint32_t c = src32[x]; + dst[x * 3 + 0] = (c >> 16) & 0xFF; + dst[x * 3 + 1] = (c >> 8) & 0xFF; + dst[x * 3 + 2] = (c >> 0) & 0xFF; + } + src32 += srcStride; + dst += dstStride * 3; + } + } else { + WARN_LOG_REPORT_ONCE(convFromBGRA, G3D, "Unable to convert from format to BGRA: %d", (int)format); } } + void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) { if (format == Draw::DataFormat::D32F) { const float *src32 = (const float *)src; From 2ab1552f2d8984d46c59a18f54ef67edf57a378b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 07:45:21 -0700 Subject: [PATCH 104/301] D3D9: Use an unordered_map for offscreen. Also cleanup. Mostly just for consistent usage. --- GPU/Directx9/FramebufferDX9.cpp | 17 ++++++----------- GPU/Directx9/FramebufferDX9.h | 27 ++++++++++----------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 6de3f054ad..9047dae2fb 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -82,12 +82,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { }; FramebufferManagerDX9::FramebufferManagerDX9(Draw::DrawContext *draw) - : FramebufferManagerCommon(draw), - drawPixelsTex_(0), - convBuf(0), - stencilUploadPS_(nullptr), - stencilUploadVS_(nullptr), - stencilUploadFailed_(false) { + : FramebufferManagerCommon(draw) { device_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE); deviceEx_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE_EX); @@ -119,8 +114,8 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { if (drawPixelsTex_) { drawPixelsTex_->Release(); } - for (auto it = offscreenSurfaces_.begin(), end = offscreenSurfaces_.end(); it != end; ++it) { - it->second.surface->Release(); + for (auto &it : offscreenSurfaces_) { + it.second.surface->Release(); } delete [] convBuf; if (stencilUploadPS_) { @@ -680,7 +675,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { int age = frameLastFramebufUsed_ - it->second.last_frame_used; if (age > FBO_OLD_AGE) { it->second.surface->Release(); - offscreenSurfaces_.erase(it++); + it = offscreenSurfaces_.erase(it); } else { ++it; } @@ -706,8 +701,8 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { } bvfbs_.clear(); - for (auto it = offscreenSurfaces_.begin(), end = offscreenSurfaces_.end(); it != end; ++it) { - it->second.surface->Release(); + for (auto &it : offscreenSurfaces_) { + it.second.surface->Release(); } offscreenSurfaces_.clear(); diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 004f7bb944..4c1498b786 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -17,9 +17,7 @@ #pragma once -#include -#include -#include +#include #include @@ -91,20 +89,20 @@ private: LPDIRECT3DDEVICE9 deviceEx_; // Used by DrawPixels - LPDIRECT3DTEXTURE9 drawPixelsTex_; + LPDIRECT3DTEXTURE9 drawPixelsTex_ = nullptr; int drawPixelsTexW_; int drawPixelsTexH_; - LPDIRECT3DVERTEXSHADER9 pFramebufferVertexShader; - LPDIRECT3DPIXELSHADER9 pFramebufferPixelShader; - LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl; + LPDIRECT3DVERTEXSHADER9 pFramebufferVertexShader = nullptr; + LPDIRECT3DPIXELSHADER9 pFramebufferPixelShader = nullptr; + LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl = nullptr; - u8 *convBuf; + u8 *convBuf = nullptr; int plainColorLoc_; - LPDIRECT3DPIXELSHADER9 stencilUploadPS_; - LPDIRECT3DVERTEXSHADER9 stencilUploadVS_; - bool stencilUploadFailed_; + LPDIRECT3DPIXELSHADER9 stencilUploadPS_ = nullptr; + LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr; + bool stencilUploadFailed_ = false; TextureCacheDX9 *textureCacheDX9_; ShaderManagerDX9 *shaderManagerDX9_; @@ -115,12 +113,7 @@ private: int last_frame_used; }; - std::map offscreenSurfaces_; - -#if 0 - AsyncPBO *pixelBufObj_; //this isn't that large - u8 currentPBO_; -#endif + std::unordered_map offscreenSurfaces_; }; }; From ad0b8435ca266943c6f6ed3c3d4c9893c89bb28d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 07:45:48 -0700 Subject: [PATCH 105/301] D3D9: Reuse BGRA conversion code. Also, couldn't have been right to skip it when the pointers equal, not sure if that case was ever being hit. --- GPU/Directx9/FramebufferDX9.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 9047dae2fb..8b6025732a 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -529,16 +529,7 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { const u32 *src32 = (const u32 *)src; if (format == GE_FORMAT_8888) { - u32 *dst32 = (u32 *)dst; - if (src == dst) { - return; - } else { - for (u32 y = 0; y < height; ++y) { - ConvertBGRA8888ToRGBA8888(dst32, src32, width); - src32 += srcStride; - dst32 += dstStride; - } - } + ConvertFromBGRA8888(dst, src, dstStride, srcStride, width, height, Draw::DataFormat::R8G8B8A8_UNORM); } else { // But here it shouldn't matter if they do intersect u16 *dst16 = (u16 *)dst; From 8f354e5312fff63a82c6599d67844475c2a0a025 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 08:08:18 -0700 Subject: [PATCH 106/301] GPU: Minor framebuffer code cleanup. --- GPU/D3D11/FramebufferManagerD3D11.cpp | 72 +++++++++------------------ GPU/D3D11/FramebufferManagerD3D11.h | 9 ---- GPU/GLES/FramebufferManagerGLES.cpp | 33 ++++-------- GPU/GLES/FramebufferManagerGLES.h | 4 -- GPU/Vulkan/FramebufferVulkan.cpp | 50 ++++++------------- GPU/Vulkan/FramebufferVulkan.h | 6 +-- 6 files changed, 51 insertions(+), 123 deletions(-) diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index cf17bdbf1b..0bfc412541 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -161,10 +161,7 @@ FramebufferManagerD3D11::~FramebufferManagerD3D11() { postInputLayout_->Release(); } - // FBO cleanup - for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { - it->second.fbo->Release(); - } + // Temp FBOs cleared by FramebufferCommon. delete[] convBuf; // Stencil cleanup @@ -266,8 +263,6 @@ void FramebufferManagerD3D11::CompilePostShader() { } void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - u8 *convBuf = nullptr; - // TODO: Check / use D3DCAPS2_DYNAMICTEXTURES? if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) { drawPixelsTex_->Release(); @@ -301,54 +296,35 @@ void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferForm D3D11_MAPPED_SUBRESOURCE map; context_->Map(drawPixelsTex_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); - convBuf = (u8*)map.pData; - - if (srcPixelFormat != GE_FORMAT_8888 || srcStride != 512) { - for (int y = 0; y < height; y++) { - switch (srcPixelFormat) { - case GE_FORMAT_565: - { - const u16_le *src = (const u16_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + map.RowPitch * y); - ConvertRGB565ToBGRA8888(dst, src, width); - } - break; - // faster - case GE_FORMAT_5551: - { - const u16_le *src = (const u16_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + map.RowPitch * y); - ConvertRGBA5551ToBGRA8888(dst, src, width); - } - break; - case GE_FORMAT_4444: - { - const u16_le *src = (const u16_le *)srcPixels + srcStride * y; - u8 *dst = (u8 *)(convBuf + map.RowPitch * y); - ConvertRGBA4444ToBGRA8888((u32 *)dst, src, width); - } + for (int y = 0; y < height; y++) { + const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; + const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)((u8 *)map.pData + map.RowPitch * y); + switch (srcPixelFormat) { + case GE_FORMAT_565: + ConvertRGB565ToBGRA8888(dst, src16, width); break; - case GE_FORMAT_8888: - { - const u32_le *src = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + map.RowPitch * y); - ConvertRGBA8888ToBGRA8888(dst, src, width); - } + case GE_FORMAT_5551: + ConvertRGBA5551ToBGRA8888(dst, src16, width); + break; + + case GE_FORMAT_4444: + ConvertRGBA4444ToBGRA8888(dst, src16, width); + break; + + case GE_FORMAT_8888: + ConvertRGBA8888ToBGRA8888(dst, src32, width); + break; + + case GE_FORMAT_INVALID: + _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); break; - } - } - } else { - for (int y = 0; y < height; y++) { - const u32_le *src = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + map.RowPitch * y); - ConvertRGBA8888ToBGRA8888(dst, src, width); } } context_->Unmap(drawPixelsTex_, 0); context_->PSSetShaderResources(0, 1, &drawPixelsTexView_); - // D3DXSaveTextureToFile("game:\\cc.png", D3DXIFF_PNG, drawPixelsTex_, NULL); } void FramebufferManagerD3D11::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { @@ -718,8 +694,8 @@ void FramebufferManagerD3D11::DestroyAllFBOs() { } bvfbs_.clear(); - for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { - it->second.fbo->Release(); + for (auto &tempFB : tempFBOs_) { + tempFB.second.fbo->Release(); } tempFBOs_.clear(); diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index 253bc8dd6e..d73b772b62 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -17,10 +17,6 @@ #pragma once -#include -#include -#include - #include // Keeps track of allocated FBOs. @@ -122,9 +118,4 @@ private: ID3D11InputLayout *postInputLayout_ = nullptr; ID3D11Buffer *postConstants_ = nullptr; static const D3D11_INPUT_ELEMENT_DESC g_PostVertexElements[2]; - -#if 0 - AsyncPBO *pixelBufObj_; //this isn't that large - u8 currentPBO_; -#endif }; diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index d27353898d..b72b90cf11 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -76,8 +76,6 @@ static const char basic_vs[] = " gl_Position = a_position;\n" "}\n"; -const int MAX_PBO = 2; - void FramebufferManagerGLES::CompileDraw2DProgram() { if (!draw2dprogram_) { std::string errorString; @@ -359,37 +357,24 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma u32 neededSize = texWidth * height * 4; u8 *convBuf = new u8[neededSize]; for (int y = 0; y < height; y++) { + const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; + const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)convBuf + texWidth * y; switch (srcPixelFormat) { case GE_FORMAT_565: - { - const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf + 4 * texWidth * y; - ConvertRGBA565ToRGBA8888((u32 *)dst, src, width); - } + ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width); break; case GE_FORMAT_5551: - { - const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf + 4 * texWidth * y; - ConvertRGBA5551ToRGBA8888((u32 *)dst, src, width); - } + ConvertRGBA5551ToRGBA8888((u32 *)dst, src16, width); break; case GE_FORMAT_4444: - { - const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf + 4 * texWidth * y; - ConvertRGBA4444ToRGBA8888((u32 *)dst, src, width); - } + ConvertRGBA4444ToRGBA8888((u32 *)dst, src16, width); break; case GE_FORMAT_8888: - { - const u8 *src = srcPixels + srcStride * 4 * y; - u8 *dst = convBuf + 4 * texWidth * y; - memcpy(dst, src, 4 * width); - } + memcpy(dst, src32, 4 * width); break; case GE_FORMAT_INVALID: @@ -752,8 +737,8 @@ void FramebufferManagerGLES::DestroyAllFBOs() { } bvfbs_.clear(); - for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { - it->second.fbo->Release(); + for (auto &tempFB : tempFBOs_) { + tempFB.second.fbo->Release(); } tempFBOs_.clear(); diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index c7ad049210..8c0be0ac1e 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -17,10 +17,6 @@ #pragma once -#include -#include -#include - #include "ext/native/thin3d/thin3d.h" // Keeps track of allocated FBOs. // Also provides facilities for drawing and later converting raw diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 171a1205a8..d44b2a9983 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -82,14 +82,7 @@ void main() { FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) : FramebufferManagerCommon(draw), - vulkan_(vulkan), - drawPixelsTex_(nullptr), - drawPixelsTexFormat_(GE_FORMAT_INVALID), - convBuf_(nullptr), - convBufSize_(0), - textureCacheVulkan_(nullptr), - shaderManagerVulkan_(nullptr), - pipelinePostShader_(VK_NULL_HANDLE) { + vulkan_(vulkan) { InitDeviceObjects(); @@ -215,7 +208,7 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor // TODO: We can just change the texture format and flip some bits around instead of this. // Could share code with the texture cache perhaps. - // Could also convert directly into the pushbuffer easily. + // TODO: Could also convert directly into the pushbuffer easily. const uint8_t *data = srcPixels; if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) { u32 neededSize = width * height * 4; @@ -226,38 +219,25 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor } data = convBuf_; for (int y = 0; y < height; y++) { + const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; + const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)convBuf_ + width * y; switch (srcPixelFormat) { case GE_FORMAT_565: - { - const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf_ + 4 * width * y; - ConvertRGBA565ToRGBA8888((u32 *)dst, src, width); - } - break; + ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width); + break; case GE_FORMAT_5551: - { - const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf_ + 4 * width * y; - ConvertRGBA5551ToRGBA8888((u32 *)dst, src, width); - } - break; + ConvertRGBA5551ToRGBA8888((u32 *)dst, src16, width); + break; case GE_FORMAT_4444: - { - const u16 *src = (const u16 *)srcPixels + srcStride * y; - u8 *dst = convBuf_ + 4 * width * y; - ConvertRGBA4444ToRGBA8888((u32 *)dst, src, width); - } - break; + ConvertRGBA4444ToRGBA8888((u32 *)dst, src16, width); + break; case GE_FORMAT_8888: - { - const u8 *src = srcPixels + srcStride * 4 * y; - u8 *dst = convBuf_ + 4 * width * y; - memcpy(dst, src, 4 * width); - } - break; + memcpy(dst, src32, 4 * width); + break; case GE_FORMAT_INVALID: _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); @@ -571,8 +551,8 @@ void FramebufferManagerVulkan::DestroyAllFBOs() { } bvfbs_.clear(); - for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { - it->second.fbo->Release(); + for (auto &tempFB : tempFBOs_) { + tempFB.second.fbo->Release(); } tempFBOs_.clear(); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index e55fb0f1eb..e7a7fa670d 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -96,9 +96,9 @@ private: u8 *convBuf_ = nullptr; u32 convBufSize_ = 0; - TextureCacheVulkan *textureCacheVulkan_; - ShaderManagerVulkan *shaderManagerVulkan_; - DrawEngineVulkan *drawEngineVulkan_; + TextureCacheVulkan *textureCacheVulkan_ = nullptr; + ShaderManagerVulkan *shaderManagerVulkan_ = nullptr; + DrawEngineVulkan *drawEngineVulkan_ = nullptr; VulkanPushBuffer *push_; enum { From 2f859425a876d42db59e0a989d6068dcff61e997 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 08:08:45 -0700 Subject: [PATCH 107/301] GPU: Use unordered_map for tempFBOs. --- GPU/Common/FramebufferCommon.cpp | 2 +- GPU/Common/FramebufferCommon.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 2d384e2ee4..db23b60868 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1100,7 +1100,7 @@ void FramebufferManagerCommon::DecimateFBOs() { int age = frameLastFramebufUsed_ - it->second.last_frame_used; if (age > FBO_OLD_AGE) { it->second.fbo->Release(); - tempFBOs_.erase(it++); + it = tempFBOs_.erase(it); } else { ++it; } diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index be1e0ae115..468701663a 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include "Common/CommonTypes.h" #include "Core/MemMap.h" @@ -412,7 +412,7 @@ protected: int last_frame_used; }; - std::map tempFBOs_; + std::unordered_map tempFBOs_; std::vector fbosToDelete_; From 4f93b1d13618e975ee019b077641d2d8f3d3f383 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 08:10:09 -0700 Subject: [PATCH 108/301] GLES: Removed unused skip copy code. Now it was just making the buffer larger for no reason. Before, it allowed us to skip the convBuf copy. --- GPU/GLES/FramebufferManagerGLES.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index b72b90cf11..62b1da391c 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -332,34 +332,24 @@ FramebufferManagerGLES::~FramebufferManagerGLES() { } void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - // Optimization: skip a copy if possible in a common case. - int texWidth = width; - if (srcPixelFormat == GE_FORMAT_8888 && width < srcStride) { - // Don't up the upload requirements too much if subimages are unsupported. - if (gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE) || width >= 480) { - texWidth = srcStride; - u1 *= (float)width / texWidth; - } - } - if (drawPixelsTex_) { render_->DeleteTexture(drawPixelsTex_); } drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D); - drawPixelsTexW_ = texWidth; + drawPixelsTexW_ = width; drawPixelsTexH_ = height; drawPixelsTexFormat_ = srcPixelFormat; // TODO: We can just change the texture format and flip some bits around instead of this. // Could share code with the texture cache perhaps. - u32 neededSize = texWidth * height * 4; + u32 neededSize = width * height * 4; u8 *convBuf = new u8[neededSize]; for (int y = 0; y < height; y++) { const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)convBuf + texWidth * y; + u32 *dst = (u32 *)convBuf + width * y; switch (srcPixelFormat) { case GE_FORMAT_565: ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width); @@ -382,7 +372,7 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma break; } } - render_->TextureImage(drawPixelsTex_, 0, texWidth, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf, GLRAllocType::NEW, false); + render_->TextureImage(drawPixelsTex_, 0, width, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, convBuf, GLRAllocType::NEW, false); render_->FinalizeTexture(drawPixelsTex_, 0, false); // TODO: Return instead? From fcabc319e15ce92554381d8f33b5b8e9ae27644b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 5 Jun 2018 21:53:32 -0700 Subject: [PATCH 109/301] Qt: Load texture replacement images. Still doesn't save, but at least it can use packs. --- Core/TextureReplacer.cpp | 68 +++++++++++++++++++++++++++++++--------- Core/TextureReplacer.h | 1 - 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index 3090df864b..fdb9254ec4 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -15,7 +15,9 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#ifndef USING_QT_UI +#ifdef USING_QT_UI +#include +#else #include #endif @@ -279,39 +281,50 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey, break; } + bool good = false; ReplacedTextureLevel level; level.fmt = ReplacedTextureFormat::F_8888; level.file = filename; #ifdef USING_QT_UI - ERROR_LOG(G3D, "Replacement texture loading not implemented for Qt"); + QImage image(filename.c_str(), "PNG"); + if (image.isNull()) { + ERROR_LOG(G3D, "Could not load texture replacement info: %s", filename.c_str()); + } else { + level.w = (image.width() * w) / newW; + level.h = (image.height() * h) / newH; + good = true; + } #else png_image png = {}; png.version = PNG_IMAGE_VERSION; FILE *fp = File::OpenCFile(filename, "rb"); - bool bad = false; if (png_image_begin_read_from_stdio(&png, fp)) { // We pad files that have been hashrange'd so they are the same texture size. level.w = (png.width * w) / newW; level.h = (png.height * h) / newH; - if (i != 0) { - // Check that the mipmap size is correct. Can't load mips of the wrong size. - if (level.w != (result->levels_[0].w >> i) || level.h != (result->levels_[0].h >> i)) { - WARN_LOG(G3D, "Replacement mipmap invalid: size=%dx%d, expected=%dx%d (level %d, '%s')", level.w, level.h, result->levels_[0].w >> i, result->levels_[0].h >> i, i, filename.c_str()); - bad = true; - } - } - if (!bad) - result->levels_.push_back(level); + good = true; } else { ERROR_LOG(G3D, "Could not load texture replacement info: %s - %s", filename.c_str(), png.message); } fclose(fp); png_image_free(&png); - if (bad) - break; // Don't try to load any more mips. #endif + + if (good && i != 0) { + // Check that the mipmap size is correct. Can't load mips of the wrong size. + if (level.w != (result->levels_[0].w >> i) || level.h != (result->levels_[0].h >> i)) { + WARN_LOG(G3D, "Replacement mipmap invalid: size=%dx%d, expected=%dx%d (level %d, '%s')", level.w, level.h, result->levels_[0].w >> i, result->levels_[0].h >> i, i, filename.c_str()); + good = false; + } + } + + if (good) + result->levels_.push_back(level); + // Otherwise, we're done loading mips (bad PNG or bad size, either way.) + else + break; } result->alphaStatus_ = ReplacedTextureAlpha::UNKNOWN; @@ -540,7 +553,32 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) { const ReplacedTextureLevel &info = levels_[level]; #ifdef USING_QT_UI - ERROR_LOG(G3D, "Replacement texture loading not implemented for Qt"); + QImage image(info.file.c_str(), "PNG"); + if (image.isNull()) { + ERROR_LOG(G3D, "Could not load texture replacement info: %s", info.file.c_str()); + return; + } + + image = image.convertToFormat(QImage::Format_ARGB32); + bool alphaFull = true; + for (int y = 0; y < image.height(); ++y) { + const QRgb *src = (const QRgb *)image.constScanLine(y); + uint8_t *outLine = (uint8_t *)out + y * rowPitch; + for (int x = 0; x < image.width(); ++x) { + outLine[x * 4 + 0] = qRed(src[x]); + outLine[x * 4 + 1] = qGreen(src[x]); + outLine[x * 4 + 2] = qBlue(src[x]); + outLine[x * 4 + 3] = qAlpha(src[x]); + // We're already scanning each pixel... + if (qAlpha(src[x]) != 255) { + alphaFull = false; + } + } + } + + if (level == 0 || !alphaFull) { + alphaStatus_ = alphaFull ? ReplacedTextureAlpha::FULL : ReplacedTextureAlpha::UNKNOWN; + } #else png_image png = {}; png.version = PNG_IMAGE_VERSION; diff --git a/Core/TextureReplacer.h b/Core/TextureReplacer.h index 49606c0164..1e4b7cdae0 100644 --- a/Core/TextureReplacer.h +++ b/Core/TextureReplacer.h @@ -43,7 +43,6 @@ enum class ReplacedTextureFormat { enum class ReplacedTextureAlpha { UNKNOWN = 0x04, FULL = 0x00, - SIMPLE = 0x08, }; // For forward comatibility, we specify the hash. From ef08c81a6f3cafa70fca4cda99a8215faf89c3dd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 08:37:09 -0700 Subject: [PATCH 110/301] Qt: Mark git-version.cpp to skip automoc. Fixes #10829. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e26ebe2227..8490e33052 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1701,7 +1701,6 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Util/ppge_atlas.h ${CORE_NEON} ${GPU_SOURCES} - git-version.cpp ext/disarm.cpp git-version.cpp) @@ -1751,7 +1750,8 @@ add_custom_command(OUTPUT something_that_never_exists -P ${CMAKE_CURRENT_SOURCE_DIR}/git-version.cmake) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/git-version.cpp - PROPERTIES GENERATED TRUE) + PROPERTIES GENERATED TRUE + SKIP_AUTOMOC ON) add_dependencies(${CoreLibName} GitVersion) set(WindowsFiles From 360e28f971bfe0d86d4b30d196c57b7b56aaaedc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 09:02:57 -0700 Subject: [PATCH 111/301] Qt: Fix build on Mac. --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8490e33052..6337db39dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,8 +273,13 @@ if(NOT MSVC) if(IOS) elseif(APPLE AND NOT CMAKE_CROSSCOMPILING) # We want C++11, so target 10.7+ - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7 -stdlib=libc++ -U__STRICT_ANSI__") + if(USING_QT_UI) + set(MACOSX_VERSION_MIN "10.8") + else() + set(MACOSX_VERSION_MIN "10.7") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MACOSX_VERSION_MIN}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MACOSX_VERSION_MIN} -stdlib=libc++ -U__STRICT_ANSI__") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") elseif(NOT ANDROID) # TODO: See if we can get rid of no-psabi From 18f0168b636855ce18bf4340861f29ca70fb644c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 09:12:36 -0700 Subject: [PATCH 112/301] Qt Debugger: Remove VFPU dialog. Being replaced by new WeBsocket based debugger with better maintenance. --- CMakeLists.txt | 5 -- Qt/Debugger/ctrlvfpuview.cpp | 93 --------------------------------- Qt/Debugger/ctrlvfpuview.h | 38 -------------- Qt/Debugger/debugger_disasm.cpp | 13 ----- Qt/Debugger/debugger_disasm.h | 4 -- Qt/Debugger/debugger_disasm.ui | 7 --- Qt/Debugger/debugger_vfpu.cpp | 38 -------------- Qt/Debugger/debugger_vfpu.h | 31 ----------- Qt/Debugger/debugger_vfpu.ui | 87 ------------------------------ 9 files changed, 316 deletions(-) delete mode 100644 Qt/Debugger/ctrlvfpuview.cpp delete mode 100644 Qt/Debugger/ctrlvfpuview.h delete mode 100644 Qt/Debugger/debugger_vfpu.cpp delete mode 100644 Qt/Debugger/debugger_vfpu.h delete mode 100644 Qt/Debugger/debugger_vfpu.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 6337db39dd..d67b65f837 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -761,7 +761,6 @@ elseif(USING_QT_UI) Qt/Debugger/debugger_displaylist.ui Qt/Debugger/debugger_memory.ui Qt/Debugger/debugger_memorytex.ui - Qt/Debugger/debugger_vfpu.ui ) qt5_wrap_ui(QT_UI_GEN ${Qt_UI}) list(APPEND NativeAppSource @@ -778,8 +777,6 @@ elseif(USING_QT_UI) Qt/Debugger/ctrlmemview.h Qt/Debugger/ctrlregisterlist.cpp Qt/Debugger/ctrlregisterlist.h - Qt/Debugger/ctrlvfpuview.cpp - Qt/Debugger/ctrlvfpuview.h Qt/Debugger/debugger_disasm.cpp Qt/Debugger/debugger_disasm.h Qt/Debugger/debugger_displaylist.cpp @@ -788,8 +785,6 @@ elseif(USING_QT_UI) Qt/Debugger/debugger_memory.h Qt/Debugger/debugger_memorytex.cpp Qt/Debugger/debugger_memorytex.h - Qt/Debugger/debugger_vfpu.cpp - Qt/Debugger/debugger_vfpu.h ) add_definitions(-DUSING_QT_UI) include_directories(${CMAKE_CURRENT_BINARY_DIR} Qt Qt/Debugger) diff --git a/Qt/Debugger/ctrlvfpuview.cpp b/Qt/Debugger/ctrlvfpuview.cpp deleted file mode 100644 index f7622d03ef..0000000000 --- a/Qt/Debugger/ctrlvfpuview.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "ctrlvfpuview.h" -#include - -#include "Core/MIPS/MIPS.h" // BAD - -CtrlVfpuView::CtrlVfpuView(QWidget *parent) : - QWidget(parent) -{ - mode = 0; -} - -void CtrlVfpuView::setMode(int newMode) -{ - mode = newMode; - redraw(); -} - -void CtrlVfpuView::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - painter.setBrush(Qt::white); - painter.setPen(Qt::white); - painter.drawRect(rect()); - - if (!cpu) - return; - - QFont normalFont("Arial", 10); - painter.setFont(normalFont); - - QPen currentPen(0xFF000000); - QBrush nullBrush(0xFFEfE8); - painter.setPen(currentPen); - painter.setBrush(nullBrush); - - enum - { - rowHeight = 15, - columnWidth = 80, - xStart = columnWidth/2, - yStart = 0 - }; - - for (int matrix = 0; matrix<8; matrix++) - { - int my = (int)(yStart + matrix * rowHeight * 5.5f); - painter.drawRect(0, my, xStart-1, rowHeight-1); - painter.drawText(3, my+rowHeight-3, QString("M%1").arg(matrix)+"00"); - painter.drawRect(xStart, my+rowHeight, columnWidth*4-1, 4*rowHeight-1); - - for (int column = 0; column<4; column++) - { - int y = my; - int x = column * columnWidth + xStart; - - painter.drawRect(x, y, columnWidth-1, rowHeight - 1); - painter.drawText(x+3, y-3+rowHeight, QString("R%1").arg(matrix)+QString("0%1").arg(column)); - - painter.drawRect(0, y+rowHeight*(column+1), xStart - 1, rowHeight - 1); - painter.drawText(3, y+rowHeight*(column+2)-3, QString("C%1").arg(matrix)+QString("%1").arg(column)+"0"); - - y+=rowHeight; - - for (int row = 0; row<4; row++) - { - float val = mipsr4k.v[column*32+row+matrix*4]; - u32 hex = *((u32*)&val); - char temp[256]; - switch (mode) - { - case 0: sprintf(temp,"%f",val); break; -// case 1: sprintf(temp,"??"); break; - case 2: sprintf(temp,"0x%08x",hex); break; - default:sprintf(temp,"%f",val); break; - } - - painter.drawText(x+3, y-3 + rowHeight, temp); - y+=rowHeight; - } - } - } - - setMinimumHeight((int)(yStart + 8 * rowHeight * 5.5f)); - setMinimumWidth(4*columnWidth+xStart); - -} - - -void CtrlVfpuView::redraw() -{ - update(); -} - diff --git a/Qt/Debugger/ctrlvfpuview.h b/Qt/Debugger/ctrlvfpuview.h deleted file mode 100644 index 5fabb3dd66..0000000000 --- a/Qt/Debugger/ctrlvfpuview.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CTRLVFPUVIEW_H -#define CTRLVFPUVIEW_H - -#include -#include "Core/Debugger/DebugInterface.h" - -class Debugger_Vfpu; -class CtrlVfpuView : public QWidget -{ - Q_OBJECT -public: - explicit CtrlVfpuView(QWidget *parent = 0); - - void setParentWindow(Debugger_Vfpu* win) - { - parentWindow = win; - } - - void setCPU(DebugInterface *deb) - { - cpu = deb; - } - DebugInterface *getCPU() - { - return cpu; - } - void setMode(int newMode); - void redraw(); -protected: - void paintEvent(QPaintEvent *); - -private: - DebugInterface *cpu; - Debugger_Vfpu* parentWindow; - int mode; -}; - -#endif // CTRLVFPUVIEW_H diff --git a/Qt/Debugger/debugger_disasm.cpp b/Qt/Debugger/debugger_disasm.cpp index f8e585e23d..dfdf2c5699 100644 --- a/Qt/Debugger/debugger_disasm.cpp +++ b/Qt/Debugger/debugger_disasm.cpp @@ -29,8 +29,6 @@ Debugger_Disasm::Debugger_Disasm(DebugInterface *_cpu, MainWindow* mainWindow_, { ui->setupUi(this); - vfpudlg = new Debugger_VFPU(_cpu, mainWindow, this); - ui->DisasmView->setWindowTitle(_cpu->GetName()); QObject::connect(ui->RegListScroll,SIGNAL(actionTriggered(int)), ui->RegList, SLOT(scrollChanged(int))); @@ -58,11 +56,6 @@ Debugger_Disasm::~Debugger_Disasm() delete ui; } -void Debugger_Disasm::ShowVFPU() -{ - vfpudlg->show(); -} - void Debugger_Disasm::Update() { ui->RegList->redraw(); @@ -108,7 +101,6 @@ void Debugger_Disasm::UpdateDialog() ui->DisasmView->setAlign(cpu->getInstructionSize(0)); ui->DisasmView->redraw(); ui->RegList->redraw(); - vfpudlg->Update(); UpdateBreakpoints(); UpdateThread(); UpdateDisplayList(); @@ -267,11 +259,6 @@ void Debugger_Disasm::ShowMemory(u32 addr) mainWindow->ShowMemory(addr); } -void Debugger_Disasm::on_vfpu_clicked() -{ - ShowVFPU(); -} - void Debugger_Disasm::on_FuncList_itemClicked(QListWidgetItem *item) { u32 addr = item->data(Qt::UserRole).toInt(); diff --git a/Qt/Debugger/debugger_disasm.h b/Qt/Debugger/debugger_disasm.h index aa06dc5bc8..d2fa1d0a54 100644 --- a/Qt/Debugger/debugger_disasm.h +++ b/Qt/Debugger/debugger_disasm.h @@ -4,7 +4,6 @@ #include "Core/MIPS/MIPSStackWalk.h" #include "Core/HLE/sceKernelThread.h" #include "Core/Debugger/DebugInterface.h" -#include "debugger_vfpu.h" #include #include #include @@ -24,7 +23,6 @@ public: ~Debugger_Disasm(); void SetDebugMode(bool _bDebug); - void ShowVFPU(); void Go(); void Step(); void StepOver(); @@ -73,7 +71,6 @@ private slots: void on_DisasmView_customContextMenuRequested(const QPoint &pos); void on_RegList_customContextMenuRequested(const QPoint &pos); - void on_vfpu_clicked(); void on_FuncList_itemClicked(QListWidgetItem *item); void on_breakpointsList_itemClicked(QTreeWidgetItem *item, int column); void on_breakpointsList_customContextMenuRequested(const QPoint &pos); @@ -92,7 +89,6 @@ private: Ui::Debugger_Disasm *ui; DebugInterface* cpu; MainWindow* mainWindow; - Debugger_VFPU* vfpudlg; u32 breakpointAddr; QTreeWidgetItem* threadRowSelected; QTreeWidgetItem* displayListRowSelected; diff --git a/Qt/Debugger/debugger_disasm.ui b/Qt/Debugger/debugger_disasm.ui index fb3b7e6e17..89f5783e78 100644 --- a/Qt/Debugger/debugger_disasm.ui +++ b/Qt/Debugger/debugger_disasm.ui @@ -122,13 +122,6 @@ - - - - Show VFPU - - - diff --git a/Qt/Debugger/debugger_vfpu.cpp b/Qt/Debugger/debugger_vfpu.cpp deleted file mode 100644 index af10c2e058..0000000000 --- a/Qt/Debugger/debugger_vfpu.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "debugger_vfpu.h" -#include "ui_debugger_vfpu.h" -#include "mainwindow.h" -#include - -Debugger_VFPU::Debugger_VFPU(DebugInterface *_cpu, MainWindow* mainWindow_, QWidget *parent) : - QDialog(parent), - ui(new Ui::Debugger_VFPU), - cpu(_cpu), - mainWindow(mainWindow_) -{ - ui->setupUi(this); - - setWindowTitle(QString("VFPU - %1").arg(cpu->GetName())); - - ui->vfpu->setCPU(_cpu); -} - -Debugger_VFPU::~Debugger_VFPU() -{ - delete ui; -} - -void Debugger_VFPU::Update() -{ - ui->vfpu->redraw(); -} - -void Debugger_VFPU::Goto(u32 addr) -{ - show(); - mainWindow->GetDialogMemory()->Goto(addr & ~3); -} - -void Debugger_VFPU::on_comboBox_currentIndexChanged(int index) -{ - ui->vfpu->setMode(index); -} diff --git a/Qt/Debugger/debugger_vfpu.h b/Qt/Debugger/debugger_vfpu.h deleted file mode 100644 index e14fc73713..0000000000 --- a/Qt/Debugger/debugger_vfpu.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef DEBUGGER_VFPU_H -#define DEBUGGER_VFPU_H - -#include "Core/Debugger/DebugInterface.h" -#include - -class MainWindow; -namespace Ui { -class Debugger_VFPU; -} - -class Debugger_VFPU : public QDialog -{ - Q_OBJECT - -public: - explicit Debugger_VFPU(DebugInterface *_cpu, MainWindow *mainWindow_, QWidget *parent = 0); - ~Debugger_VFPU(); - - void Update(); - void Goto(u32 addr); -private slots: - void on_comboBox_currentIndexChanged(int index); - -private: - Ui::Debugger_VFPU *ui; - DebugInterface* cpu; - MainWindow* mainWindow; -}; - -#endif // DEBUGGER_VFPU_H diff --git a/Qt/Debugger/debugger_vfpu.ui b/Qt/Debugger/debugger_vfpu.ui deleted file mode 100644 index acf1f04dba..0000000000 --- a/Qt/Debugger/debugger_vfpu.ui +++ /dev/null @@ -1,87 +0,0 @@ - - - Debugger_VFPU - - - - 0 - 0 - 400 - 300 - - - - VFPU - - - - - - - - - Float - - - - - HalfFloat - - - - - Hex - - - - - Bytes - - - - - Shorts - - - - - Ints - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::StrongFocus - - - - - - - - CtrlVfpuView - QWidget -
ctrlvfpuview.h
- 1 -
-
- - -
From c3213ade257d4a9cd0b3e679c64ae22d02731dcf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 10:46:17 -0700 Subject: [PATCH 113/301] Qt Debugger: Remove disassembly. Being replaced by WebSocket based debugger which is better maintained. --- CMakeLists.txt | 7 - Qt/Debugger/ctrldisasmview.cpp | 422 ---------------------- Qt/Debugger/ctrldisasmview.h | 100 ------ Qt/Debugger/ctrlregisterlist.cpp | 335 ----------------- Qt/Debugger/ctrlregisterlist.h | 77 ---- Qt/Debugger/debugger_disasm.cpp | 594 ------------------------------- Qt/Debugger/debugger_disasm.h | 98 ----- Qt/Debugger/debugger_disasm.ui | 523 --------------------------- Qt/QtHost.h | 5 +- Qt/mainwindow.cpp | 25 +- Qt/mainwindow.h | 4 - Windows/PPSSPP.vcxproj | 41 +-- Windows/PPSSPP.vcxproj.filters | 37 +- 13 files changed, 7 insertions(+), 2261 deletions(-) delete mode 100644 Qt/Debugger/ctrldisasmview.cpp delete mode 100644 Qt/Debugger/ctrldisasmview.h delete mode 100644 Qt/Debugger/ctrlregisterlist.cpp delete mode 100644 Qt/Debugger/ctrlregisterlist.h delete mode 100644 Qt/Debugger/debugger_disasm.cpp delete mode 100644 Qt/Debugger/debugger_disasm.h delete mode 100644 Qt/Debugger/debugger_disasm.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index d67b65f837..48d9ae691a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -757,7 +757,6 @@ elseif(USING_QT_UI) find_package(Qt5 COMPONENTS Multimedia) endif(NOT SDL2_FOUND) set(Qt_UI - Qt/Debugger/debugger_disasm.ui Qt/Debugger/debugger_displaylist.ui Qt/Debugger/debugger_memory.ui Qt/Debugger/debugger_memorytex.ui @@ -771,14 +770,8 @@ elseif(USING_QT_UI) Qt/QtHost.h Qt/mainwindow.cpp Qt/mainwindow.h - Qt/Debugger/ctrldisasmview.cpp - Qt/Debugger/ctrldisasmview.h Qt/Debugger/ctrlmemview.cpp Qt/Debugger/ctrlmemview.h - Qt/Debugger/ctrlregisterlist.cpp - Qt/Debugger/ctrlregisterlist.h - Qt/Debugger/debugger_disasm.cpp - Qt/Debugger/debugger_disasm.h Qt/Debugger/debugger_displaylist.cpp Qt/Debugger/debugger_displaylist.h Qt/Debugger/debugger_memory.cpp diff --git a/Qt/Debugger/ctrldisasmview.cpp b/Qt/Debugger/ctrldisasmview.cpp deleted file mode 100644 index af496e30a8..0000000000 --- a/Qt/Debugger/ctrldisasmview.cpp +++ /dev/null @@ -1,422 +0,0 @@ -#include "ctrldisasmview.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "math.h" - -#include "debugger_disasm.h" -#include "Core/Debugger/SymbolMap.h" - -namespace -{ - u32 halfAndHalf(u32 a, u32 b) - { - return ((a>>1)&0x7f7f7f7f) + ((b>>1)&0x7f7f7f7f); - } -} - -CtrlDisAsmView::CtrlDisAsmView(QWidget *parent) : - QWidget(parent) -{ - curAddress=0; - rowHeight=14; - align=2; - selecting=false; - showHex=false; -} - -void CtrlDisAsmView::keyPressEvent(QKeyEvent *e) -{ - int page=(rect().bottom()/rowHeight)/2-1; - - switch (e->key()) - { - case Qt::Key_Down: curAddress += align; break; - case Qt::Key_Up: curAddress -= align; break; - case Qt::Key_PageDown: curAddress += page*align; break; - case Qt::Key_PageUp: curAddress -= page*align; break; - default: QWidget::keyPressEvent(e); break; - } - - update(); -} - -void CtrlDisAsmView::mousePressEvent(QMouseEvent *e) -{ - int x = e->pos().x(); - int y = e->pos().y(); - if (x>16) - { - oldSelection=selection; - selection=yToAddress(y); - bool oldselecting=selecting; - selecting=true; - if (!oldselecting || (selection!=oldSelection)) - redraw(); - } - else - { - debugger->toggleBreakpoint(yToAddress(y)); - parentWindow->Update(); - redraw(); - } -} - -void CtrlDisAsmView::wheelEvent(QWheelEvent* e) -{ - int numDegrees = e->delta() / 8; - int numSteps = numDegrees / 15; - if (e->orientation() == Qt::Vertical) - { - curAddress -= numSteps*align; - update(); - } -} - -void CtrlDisAsmView::redraw() -{ - update(); -} - -void CtrlDisAsmView::contextMenu(const QPoint &pos) -{ - QMenu menu(this); - - QAction *copyAddress = new QAction(tr("Copy &address"), this); - connect(copyAddress, SIGNAL(triggered()), this, SLOT(CopyAddress())); - menu.addAction(copyAddress); - - QAction *copyInstrHex = new QAction(tr("Copy instruction (&hex)"), this); - connect(copyInstrHex, SIGNAL(triggered()), this, SLOT(CopyInstrHex())); - menu.addAction(copyInstrHex); - - QAction *copyInstrDisAsm = new QAction(tr("Copy instruction (&disasm)"), this); - connect(copyInstrDisAsm, SIGNAL(triggered()), this, SLOT(CopyInstrDisAsm())); - menu.addAction(copyInstrDisAsm); - - menu.addSeparator(); - - QAction *runToHere = new QAction(tr("&Run to here"), this); - connect(runToHere, SIGNAL(triggered()), this, SLOT(RunToHere())); - menu.addAction(runToHere); - - QAction *setNextStatement = new QAction(tr("&Set Next Statement"), this); - connect(setNextStatement, SIGNAL(triggered()), this, SLOT(SetNextStatement())); - menu.addAction(setNextStatement); - - QAction *toggleBreakpoint = new QAction(tr("&Toggle breakpoint"), this); - connect(toggleBreakpoint, SIGNAL(triggered()), this, SLOT(ToggleBreakpoint())); - menu.addAction(toggleBreakpoint); - - QAction *followBranch = new QAction(tr("&Follow branch"), this); - connect(followBranch, SIGNAL(triggered()), this, SLOT(FollowBranch())); - menu.addAction(followBranch); - - menu.addSeparator(); - - //QAction *showDynarecResults = new QAction(tr("&Show Dynarec Results"), this); - //connect(showDynarecResults, SIGNAL(triggered()), this, SLOT(ShowDynarecResults())); - //menu.addAction(showDynarecResults); - - QAction *goToMemoryView = new QAction(tr("Go to in &Memory View"), this); - connect(goToMemoryView, SIGNAL(triggered()), this, SLOT(GoToMemoryView())); - menu.addAction(goToMemoryView); - - menu.addSeparator(); - - //QAction *killFunction = new QAction(tr("&Kill function"), this); - //connect(killFunction, SIGNAL(triggered()), this, SLOT(KillFunction())); - //menu.addAction(killFunction); - - QAction *renameFunction = new QAction(tr("&Rename function..."), this); - connect(renameFunction, SIGNAL(triggered()), this, SLOT(RenameFunction())); - menu.addAction(renameFunction); - - - menu.exec( mapToGlobal(pos)); -} - -void CtrlDisAsmView::GoToMemoryView() -{ - parentWindow->ShowMemory(selection); -} - -void CtrlDisAsmView::CopyAddress() -{ - QApplication::clipboard()->setText(QString("%1").arg(selection,8,16,QChar('0'))); -} - -void CtrlDisAsmView::CopyInstrDisAsm() -{ - QApplication::clipboard()->setText(debugger->disasm(selection,align)); -} - -void CtrlDisAsmView::CopyInstrHex() -{ - QApplication::clipboard()->setText(QString("%1").arg(debugger->readMemory(selection),8,16,QChar('0'))); -} - -void CtrlDisAsmView::SetNextStatement() -{ - debugger->setPC(selection); - redraw(); -} - -void CtrlDisAsmView::ToggleBreakpoint() -{ - debugger->toggleBreakpoint(selection); - parentWindow->Update(); - redraw(); -} - -void CtrlDisAsmView::FollowBranch() -{ - const char *temp = debugger->disasm(selection,align);; - const char *mojs=strstr(temp,"->$"); - if (mojs) - { - u32 dest; - sscanf(mojs+3,"%08x",&dest); - if (dest) - { - marker = selection; - gotoAddr(dest); - } - } -} - -void CtrlDisAsmView::RunToHere() -{ - debugger->setBreakpoint(selection); - debugger->runToBreakpoint(); - redraw(); -} - -void CtrlDisAsmView::RenameFunction() -{ - u32 funcBegin = g_symbolMap->GetFunctionStart(curAddress); - if (funcBegin != (u32)-1) - { - QString name = QString::fromStdString(g_symbolMap->GetLabelString(funcBegin)); - bool ok; - QString newname = QInputDialog::getText(this, tr("New function name"), - tr("New function name:"), QLineEdit::Normal, - name, &ok); - if (ok && !newname.isEmpty()) - { - g_symbolMap->SetLabelName(newname.toStdString().c_str(),funcBegin); - redraw(); - parentWindow->NotifyMapLoaded(); - } - } - else - { - QMessageBox::information(this,tr("Warning"),tr("No symbol selected"),QMessageBox::Ok); - } -} - -void CtrlDisAsmView::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - painter.setBrush(Qt::white); - painter.setPen(Qt::white); - painter.drawRect(rect()); - - struct branch - { - int src,dst,srcAddr; - bool conditional; - }; - branch branches[256]; - int numBranches=0; - - int width = rect().width(); - int numRows=(rect().height()/rowHeight); - - QColor bgColor(0xFFFFFFFF); - QPen nullPen(bgColor); - QPen currentPen(QColor(0,0,0)); - QPen selPen(QColor(0xFF808080)); - QPen condPen(QColor(0xFFFF3020)); - - QBrush lbr; - lbr.setColor(bgColor); - QBrush currentBrush(QColor(0xFFFFEfE8)); - QBrush pcBrush(QColor(0xFF70FF70)); - - QFont normalFont("Arial", 10); - QFont boldFont("Arial", 10); - QFont alignedFont("Monospace", 10); - alignedFont.setStyleHint(QFont::Monospace); - boldFont.setBold(true); - painter.setFont(normalFont); - - - QImage breakPoint(":/resources/breakpoint.ico"); - int i; - curAddress&=~(align-1); - - align=(debugger->getInstructionSize(0)); - for (i=0; i<=numRows; i++) - { - unsigned int address=curAddress + (i-(numRows/2))*align; - - int rowY1 = rect().top() + rowHeight*i; - int rowY2 = rect().top() + rowHeight*i + rowHeight - 1; - - lbr.setColor((unsigned int)marker == address ? QColor(0xFFFFEEE0) : QColor(debugger->getColor(address))); - QColor bg = lbr.color(); - painter.setBrush(currentBrush); - painter.setPen(nullPen); - painter.drawRect(0,rowY1,16-1,rowY2-rowY1); - - if (selecting && address == (unsigned int)selection) - painter.setPen(selPen); - else - { - if(i==numRows/2) - painter.setPen(currentPen); - else - painter.setPen(bg); - } - painter.setBrush(QBrush(bg)); - - if (address == debugger->getPC()) - { - painter.setBrush(pcBrush); - } - - painter.drawRect(16,rowY1,width-16-1,rowY2-rowY1); - painter.setBrush(currentBrush); - QPen textPen(QColor(halfAndHalf(bg.rgba(),0))); - painter.setPen(textPen); - painter.setFont(alignedFont); - painter.drawText(17,rowY1-3+rowHeight,QString("%1").arg(address,8,16,QChar('0'))); - painter.setFont(normalFont); - textPen.setColor(QColor(0xFF000000)); - painter.setPen(textPen); - if (debugger->isAlive()) - { - const char *dizz = debugger->disasm(address, align); - char dis[512]; - strcpy(dis, dizz); - char *dis2 = strchr(dis,'\t'); - char desc[256]=""; - if (dis2) - { - *dis2=0; - dis2++; - const char *mojs=strstr(dis2,"->$"); - if (mojs) - { - for (int i=0; i<8; i++) - { - bool found=false; - for (int j=0; j<22; j++) - { - if (mojs[i+3]=="0123456789ABCDEFabcdef"[j]) - found=true; - } - if (!found) - { - mojs=0; - break; - } - } - } - if (mojs) - { - unsigned int offs; - sscanf(mojs+3,"%08x",&offs); - branches[numBranches].src=rowY1 + rowHeight/2; - branches[numBranches].srcAddr=address/align; - branches[numBranches].dst=(int)(rowY1+((s64)offs-(s64)address)*rowHeight/align + rowHeight/2); - branches[numBranches].conditional = (dis[1]!=0); //unconditional 'b' branch - numBranches++; - const char *t = debugger->getDescription(offs).c_str(); - if (memcmp(t,"z_",2)==0) - t+=2; - if (memcmp(t,"zz_",3)==0) - t+=3; - sprintf(desc,"-->%s", t); - textPen.setColor(QColor(0xFF600060)); - painter.setPen(textPen); - } - else - { - textPen.setColor(QColor(0xFF000000)); - painter.setPen(textPen); - } - painter.drawText(149,rowY1-3+rowHeight,QString(dis2)); - } - textPen.setColor(QColor(0xFF007000)); - painter.setPen(textPen); - painter.setFont(boldFont); - painter.drawText(84,rowY1-3+rowHeight,QString(dis)); - painter.setFont(normalFont); - if (desc[0]==0) - { - const char *t = debugger->getDescription(address).c_str(); - if (memcmp(t,"z_",2)==0) - t+=2; - if (memcmp(t,"zz_",3)==0) - t+=3; - strcpy(desc,t); - } - if (memcmp(desc,"-->",3) == 0) - { - textPen.setColor(QColor(0xFF0000FF)); - painter.setPen(textPen); - } - else - { - textPen.setColor(halfAndHalf(halfAndHalf(bg.rgba(),0),bg.rgba())); - painter.setPen(textPen); - } - if (strlen(desc)) - painter.drawText(std::max(280,width/3+190),rowY1-3+rowHeight,QString(desc)); - if (debugger->isBreakpoint(address)) - { - painter.drawImage(2,rowY1+2,breakPoint); - } - } - } - for (i=0; i-200) - { - painter.drawLine(curPos, QPoint(x+2,branches[i].src)); - curPos = QPoint(x+2,branches[i].src); - painter.drawLine(curPos, QPoint(x+2,branches[i].dst)); - curPos = QPoint(x+2,branches[i].dst); - painter.drawLine(curPos, QPoint(x-4,branches[i].dst)); - - curPos = QPoint(x,branches[i].dst-4); - painter.drawLine(curPos, QPoint(x-4,branches[i].dst)); - curPos = QPoint(x-4,branches[i].dst); - painter.drawLine(curPos, QPoint(x+1,branches[i].dst+5)); - } - else - { - painter.drawLine(curPos, QPoint(x+4,branches[i].src)); - } - } -} - -int CtrlDisAsmView::yToAddress(int y) -{ - //int ydiff=y - rect().bottom()/2;//-rowHeight/2; - int ydiff=(int)(floor((float)y / (float)rowHeight)); - ydiff -= (rect().height()/rowHeight)/2; - return curAddress + ydiff *align; -} diff --git a/Qt/Debugger/ctrldisasmview.h b/Qt/Debugger/ctrldisasmview.h deleted file mode 100644 index 8846ad40db..0000000000 --- a/Qt/Debugger/ctrldisasmview.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef CTRLDISASMVIEW_H -#define CTRLDISASMVIEW_H - -#include -#include "Core/Debugger/DebugInterface.h" - -class Debugger_Disasm; - -class CtrlDisAsmView : public QWidget -{ - Q_OBJECT -public: - explicit CtrlDisAsmView(QWidget *parent = 0); - - void redraw(); - void setAlign(int l) - { - align=l; - } - - void setParentWindow(Debugger_Disasm* win) - { - parentWindow = win; - } - - void setDebugger(DebugInterface *deb) - { - debugger=deb; - curAddress=debugger->getPC(); - align=debugger->getInstructionSize(0); - } - DebugInterface *getDebugger() - { - return debugger; - } - void gotoAddr(unsigned int addr) - { - curAddress=addr&(~(align-1)); - redraw(); - } - void gotoPC() - { - curAddress=debugger->getPC()&(~(align-1)); - redraw(); - } - unsigned int getSelection() - { - return curAddress; - } - - void setShowMode(bool s) - { - showHex=s; - } - - void toggleBreakpoint() - { - debugger->toggleBreakpoint(curAddress); - redraw(); - } - - void contextMenu(const QPoint& pos); -protected: - void paintEvent(QPaintEvent *); - void mousePressEvent(QMouseEvent *e); - void keyPressEvent(QKeyEvent *); - void wheelEvent(QWheelEvent *e); - -public slots: - void CopyAddress(); - void CopyInstrDisAsm(); - void SetNextStatement(); - void FollowBranch(); - void CopyInstrHex(); - void RunToHere(); - void ToggleBreakpoint(); - void GoToMemoryView(); - void RenameFunction(); - -private: - int yToAddress(int y); - - int curAddress; - int align; - - int rowHeight; - int selection; - int marker; - int oldSelection; - bool selectionChanged; - bool selecting; - bool hasFocus; - bool showHex; - - DebugInterface *debugger; - Debugger_Disasm* parentWindow; - -}; - -#endif // CTRLDISASMVIEW_H diff --git a/Qt/Debugger/ctrlregisterlist.cpp b/Qt/Debugger/ctrlregisterlist.cpp deleted file mode 100644 index 29184ee1ab..0000000000 --- a/Qt/Debugger/ctrlregisterlist.cpp +++ /dev/null @@ -1,335 +0,0 @@ -#include "ctrlregisterlist.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "debugger_disasm.h" - -CtrlRegisterList::CtrlRegisterList(QWidget *parent) : - QWidget(parent) -{ - rowHeight = 14; - selecting=false; - selection=0; - category=0; - showHex=false; - cpu=0; - lastPC = 0; - lastCat0Values = NULL; - changedCat0Regs = NULL; - curVertOffset = 0; - -} - -CtrlRegisterList::~CtrlRegisterList() -{ - if (lastCat0Values != NULL) - delete [] lastCat0Values; - if (changedCat0Regs != NULL) - delete [] changedCat0Regs; -} - -void CtrlRegisterList::redraw() -{ - update(); -} - -void CtrlRegisterList::scrollChanged(int action) -{ - QScrollBar *bar = findChild("RegListScroll"); - switch(action) - { - case QScrollBar::SliderSingleStepAdd: - curVertOffset++; - break; - case QScrollBar::SliderSingleStepSub: - curVertOffset--; - break; - case QScrollBar::SliderPageStepAdd: - curVertOffset+= 4; - break; - case QScrollBar::SliderPageStepSub: - curVertOffset-= 4; - break; - case QScrollBar::SliderMove: - curVertOffset = bar->sliderPosition(); - break; - default: - break; - } - redraw(); -} - - -void CtrlRegisterList::keyPressEvent(QKeyEvent *e) -{ - switch (e->key()) - { - case Qt::Key_Down: selection += 1; break; - case Qt::Key_Up: selection -= 1; break; - case Qt::Key_PageDown: selection += 4; break; - case Qt::Key_PageUp: selection -= 4; break; - default: QWidget::keyPressEvent(e); break; - } - - int maxRowsDisplay =rect().bottom()/rowHeight - 1; - curVertOffset = std::min(std::max(curVertOffset, selection-maxRowsDisplay),selection); - update(); -} - -void CtrlRegisterList::mousePressEvent(QMouseEvent *e) -{ - int x = e->pos().x(); - int y = e->pos().y(); - if (x>16) - { - oldSelection=selection; - - if (y>rowHeight) - { - selection=yToIndex(y); - bool oldselecting=selecting; - selecting=true; - if (!oldselecting || (selection!=oldSelection)) - redraw(); - } - else - { - int lastCat = category; - category = (x*cpu->GetNumCategories())/(rect().width()); - if (category<0) category=0; - if (category>=cpu->GetNumCategories()) - category=cpu->GetNumCategories()-1; - if (category!=lastCat) - { - curVertOffset = 0; - redraw(); - } - } - } - else - { - redraw(); - } -} - -void CtrlRegisterList::wheelEvent(QWheelEvent* e) -{ - int numDegrees = e->delta() / 8; - int numSteps = numDegrees / 15; - if (e->orientation() == Qt::Vertical) - { - curVertOffset -= numSteps; - update(); - } -} - - -void CtrlRegisterList::paintEvent(QPaintEvent *) -{ - - int numRowsTotal = cpu->GetNumRegsInCategory(category); - int maxRowsDisplay =rect().bottom()/rowHeight - 1; - - selection = std::min(std::max(selection,0),numRowsTotal); - curVertOffset = std::max(std::min(curVertOffset,numRowsTotal - maxRowsDisplay),0); - - QScrollBar *bar = findChild("RegListScroll"); - if(bar) - { - bar->setMinimum(0); - bar->setMaximum(numRowsTotal - maxRowsDisplay); - bar->setPageStep(1); - bar->setValue(curVertOffset); - } - - - QPainter painter(this); - painter.setBrush(Qt::white); - painter.setPen(Qt::white); - painter.drawRect(rect()); - - if (!cpu) - return; - - QFont normalFont = QFont("Arial", 10); - painter.setFont(normalFont); - - int width = rect().width(); - - QColor bgColor(0xffffff); - QPen nullPen(bgColor); - QPen currentPen(QColor(0xFF000000)); - QPen selPen(0x808080); - QPen textPen; - - QBrush lbr; - lbr.setColor(bgColor); - QBrush nullBrush(bgColor); - QBrush currentBrush(0xFFEfE8); - QBrush pcBrush(0x70FF70); - - int nc = cpu->GetNumCategories(); - for (int i=0; iGetCategoryName(i); - painter.setPen(currentPen); - painter.drawText(width*i/nc+1,-3+rowHeight,name); - } - - int numRows=rect().bottom()/rowHeight; - - for (int i=curVertOffset; iGetPC()) - { - for (int i = 0, n = cpu->GetNumRegsInCategory(0); i < n; ++i) - { - u32 v = cpu->GetRegValue(0, i); - changedCat0Regs[i] = v != lastCat0Values[i]; - lastCat0Values[i] = v; - } - lastPC = cpu->GetPC(); - } - - painter.setBrush(currentBrush); - if (iGetNumRegsInCategory(category)) - { - QString regName = cpu->GetRegName(category,i); - textPen.setColor(0x600000); - painter.setPen(textPen); - painter.drawText(17,rowY1-3+rowHeight,regName); - textPen.setColor(0xFF000000); - painter.setPen(textPen); - - char temp[256]; - cpu->PrintRegValue(category,i,temp); - if (category == 0 && changedCat0Regs[i]) - { - textPen.setColor(0x0000FF); - painter.setPen(textPen); - } - else - { - textPen.setColor(0x004000); - painter.setPen(textPen); - } - painter.drawText(77,rowY1-3+rowHeight,temp); - } - } -} - -int CtrlRegisterList::yToIndex(int y) -{ - int n = (y/rowHeight) - 1 + curVertOffset; - if (n<0) n=0; - return n; -} - -void CtrlRegisterList::contextMenu(const QPoint &pos) -{ - QMenu menu(this); - - QAction *gotoMemory = new QAction(tr("Go to in &memory view"), this); - connect(gotoMemory, SIGNAL(triggered()), this, SLOT(GotoMemory())); - menu.addAction(gotoMemory); - - QAction *gotoDisAsm = new QAction(tr("Go to in &disasm"), this); - connect(gotoDisAsm, SIGNAL(triggered()), this, SLOT(GotoDisAsm())); - menu.addAction(gotoDisAsm); - - menu.addSeparator(); - - QAction *copyValue = new QAction(tr("&Copy value"), this); - connect(copyValue, SIGNAL(triggered()), this, SLOT(CopyValue())); - menu.addAction(copyValue); - - QAction *change = new QAction(tr("C&hange..."), this); - connect(change, SIGNAL(triggered()), this, SLOT(Change())); - menu.addAction(change); - - menu.exec( mapToGlobal(pos)); -} - -void CtrlRegisterList::GotoMemory() -{ - int cat = category; - int reg = selection; - if (selection >= cpu->GetNumRegsInCategory(cat)) - return; - - u32 val = cpu->GetRegValue(cat,reg); - - parentWindow->ShowMemory(val); -} - -void CtrlRegisterList::GotoDisAsm() -{ - int cat = category; - int reg = selection; - if (selection >= cpu->GetNumRegsInCategory(cat)) - return; - - u32 val = cpu->GetRegValue(cat,reg); - - emit GotoDisasm(val); -} - -void CtrlRegisterList::CopyValue() -{ - int cat = category; - int reg = selection; - if (selection >= cpu->GetNumRegsInCategory(cat)) - return; - - u32 val = cpu->GetRegValue(cat,reg); - - QApplication::clipboard()->setText(QString("%1").arg(val,8,16,QChar('0'))); -} - -void CtrlRegisterList::Change() -{ - int cat = category; - int reg = selection; - if (selection >= cpu->GetNumRegsInCategory(cat)) - return; - - u32 val = cpu->GetRegValue(cat,reg); - - bool ok; - QString text = QInputDialog::getText(this, tr("Set new value"), - tr("Set new value:"), QLineEdit::Normal, - QString::number(val), &ok); - if (ok && !text.isEmpty()) - { - cpu->SetRegValue(cat,reg,text.toInt()); - redraw(); - } -} diff --git a/Qt/Debugger/ctrlregisterlist.h b/Qt/Debugger/ctrlregisterlist.h deleted file mode 100644 index ca3c498d9a..0000000000 --- a/Qt/Debugger/ctrlregisterlist.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef CTRLREGISTERLIST_H -#define CTRLREGISTERLIST_H - -#include -#include "Core/Debugger/DebugInterface.h" - -class Debugger_Disasm; -class CtrlRegisterList : public QWidget -{ - Q_OBJECT -public: - explicit CtrlRegisterList(QWidget *parent = 0); - - - void setParentWindow(Debugger_Disasm* win) - { - parentWindow = win; - } - - void setCPU(DebugInterface *deb) - { - cpu = deb; - - int regs = cpu->GetNumRegsInCategory(0); - lastCat0Values = new u32[regs]; - changedCat0Regs = new bool[regs]; - memset(lastCat0Values, 0, regs * sizeof(u32)); - memset(changedCat0Regs, 0, regs * sizeof(bool)); - } - DebugInterface *getCPU() - { - return cpu; - } - ~CtrlRegisterList(); - void contextMenu(const QPoint &pos); -protected: - void paintEvent(QPaintEvent *); - void keyPressEvent(QKeyEvent *e); - void mousePressEvent(QMouseEvent *e); - void wheelEvent(QWheelEvent *e); -signals: - void GotoDisasm(u32); - -public slots: - void redraw(); - void scrollChanged(int); - - void GotoDisAsm(); - void CopyValue(); - void Change(); - void GotoMemory(); - -private: - int yToIndex(int y); - - int rowHeight; - int selection; - int marker; - int category; - - int oldSelection; - - bool selectionChanged; - bool selecting; - bool hasFocus; - bool showHex; - DebugInterface *cpu; - int curVertOffset; - - u32 lastPC; - u32 *lastCat0Values; - bool *changedCat0Regs; - - Debugger_Disasm* parentWindow; -}; - -#endif // CTRLREGISTERLIST_H diff --git a/Qt/Debugger/debugger_disasm.cpp b/Qt/Debugger/debugger_disasm.cpp deleted file mode 100644 index dfdf2c5699..0000000000 --- a/Qt/Debugger/debugger_disasm.cpp +++ /dev/null @@ -1,594 +0,0 @@ -#include -#include - -#include - -#include "debugger_disasm.h" -#include "ui_debugger_disasm.h" -#include "Core/Debugger/DebugInterface.h" -#include "Core/Debugger/SymbolMap.h" -#include "ctrldisasmview.h" -#include "Core/Debugger/Breakpoints.h" -#include "Core/HLE/HLE.h" -#include "Core/CoreTiming.h" -#include "mainwindow.h" -#include "ctrlregisterlist.h" -#include "base/stringutil.h" -#include "Core/Debugger/SymbolMap.h" -#include "GPU/GPUState.h" -#include "GPU/GPUInterface.h" -#include "GPU/GeDisasm.h" -#include "GPU/Common/GPUDebugInterface.h" -#include "Core/Host.h" - -Debugger_Disasm::Debugger_Disasm(DebugInterface *_cpu, MainWindow* mainWindow_, QWidget *parent) : - QDialog(parent), - ui(new Ui::Debugger_Disasm), - cpu(_cpu), - mainWindow(mainWindow_) -{ - ui->setupUi(this); - - ui->DisasmView->setWindowTitle(_cpu->GetName()); - - QObject::connect(ui->RegListScroll,SIGNAL(actionTriggered(int)), ui->RegList, SLOT(scrollChanged(int))); - QObject::connect(ui->RegList,SIGNAL(GotoDisasm(u32)),this,SLOT(Goto(u32))); - QObject::connect(this, SIGNAL(UpdateCallstack_()), this, SLOT(UpdateCallstackGUI())); - QObject::connect(this, SIGNAL(UpdateDisplayList_()), this, SLOT(UpdateDisplayListGUI())); - QObject::connect(this, SIGNAL(UpdateBreakpoints_()), this, SLOT(UpdateBreakpointsGUI())); - QObject::connect(this, SIGNAL(UpdateThread_()), this, SLOT(UpdateThreadGUI())); - - CtrlDisAsmView *ptr = ui->DisasmView; - ptr->setDebugger(cpu); - ptr->setParentWindow(this); - ptr->gotoAddr(0x00000000); - - CtrlRegisterList *rl = ui->RegList; - rl->setParentWindow(this); - rl->setCPU(cpu); - - FillFunctions(); - -} - -Debugger_Disasm::~Debugger_Disasm() -{ - delete ui; -} - -void Debugger_Disasm::Update() -{ - ui->RegList->redraw(); - mainWindow->updateMenus(); - UpdateDialog(); -} - -void Debugger_Disasm::Go() -{ - SetDebugMode(false); - Core_EnableStepping(false); - mainWindow->updateMenus(); -} - -void Debugger_Disasm::Step() -{ - Core_DoSingleStep(); - _dbg_update_(); -} - -void Debugger_Disasm::StepOver() -{ - SetDebugMode(false); - CBreakPoints::AddBreakPoint(cpu->GetPC()+cpu->getInstructionSize(0),true); - _dbg_update_(); - Core_EnableStepping(false); - mainWindow->updateMenus(); -} - -void Debugger_Disasm::StepHLE() -{ - hleDebugBreak(); - SetDebugMode(false); - _dbg_update_(); - Core_EnableStepping(false); - mainWindow->updateMenus(); -} - -void Debugger_Disasm::UpdateDialog() -{ - if(!isVisible()) - return; - ui->DisasmView->setAlign(cpu->getInstructionSize(0)); - ui->DisasmView->redraw(); - ui->RegList->redraw(); - UpdateBreakpoints(); - UpdateThread(); - UpdateDisplayList(); - UpdateCallstack(); - - char tempTicks[24]; - sprintf(tempTicks, "%lld", CoreTiming::GetTicks()); - ui->debugCount->setText(QString("Ctr : ") + tempTicks); - - if(mainWindow->GetDialogMemory()) - mainWindow->GetDialogMemory()->Update(); - -} - -void Debugger_Disasm::Stop() -{ - SetDebugMode(true); - Core_EnableStepping(true); - _dbg_update_(); - mainWindow->updateMenus(); - UpdateDialog(); -} - -void Debugger_Disasm::Skip() -{ - CtrlDisAsmView *ptr = ui->DisasmView; - - cpu->SetPC(cpu->GetPC() + cpu->getInstructionSize(0)); - ptr->gotoPC(); - UpdateDialog(); -} - -void Debugger_Disasm::GotoPC() -{ - CtrlDisAsmView *ptr = ui->DisasmView; - - ptr->gotoPC(); - UpdateDialog(); -} - -void Debugger_Disasm::GotoLR() -{ - CtrlDisAsmView *ptr = ui->DisasmView; - - ptr->gotoAddr(cpu->GetLR()); -} - -void Debugger_Disasm::SetDebugMode(bool _bDebug) -{ - if (_bDebug) - { - ui->Go->setEnabled(true); - ui->StepInto->setEnabled(true); - ui->StepOver->setEnabled(false); // Crash, so disable for now - ui->NextHLE->setEnabled(true); - ui->Stop->setEnabled(false); - ui->Skip->setEnabled(true); - CtrlDisAsmView *ptr = ui->DisasmView; - ptr->gotoPC(); - UpdateDialog(); - } - else - { - ui->Go->setEnabled(false); - ui->StepInto->setEnabled(false); - ui->StepOver->setEnabled(false); - ui->NextHLE->setEnabled(false); - ui->Stop->setEnabled(true); - ui->Skip->setEnabled(false); - CtrlRegisterList *reglist = ui->RegList; - reglist->redraw(); - } -} - -void Debugger_Disasm::Goto(u32 addr) -{ - CtrlDisAsmView *ptr = ui->DisasmView; - ptr->gotoAddr(addr); - ptr->redraw(); -} - -void Debugger_Disasm::on_GotoPc_clicked() -{ - GotoPC(); -} - -void Debugger_Disasm::on_Go_clicked() -{ - Go(); -} - -void Debugger_Disasm::on_Stop_clicked() -{ - Stop(); -} - -void Debugger_Disasm::on_StepInto_clicked() -{ - Step(); -} - -void Debugger_Disasm::on_StepOver_clicked() -{ - StepOver(); -} - -void Debugger_Disasm::on_Skip_clicked() -{ - Skip(); -} - -void Debugger_Disasm::on_NextHLE_clicked() -{ - StepHLE(); -} - -void Debugger_Disasm::on_GotoLr_clicked() -{ - GotoLR(); -} - -void Debugger_Disasm::on_GotoInt_currentIndexChanged(int index) -{ - CtrlDisAsmView *ptr = ui->DisasmView; - u32 addr = ui->GotoInt->itemData(index,Qt::UserRole).toInt(); - if (addr != 0xFFFFFFFF) - ptr->gotoAddr(addr); -} - -void Debugger_Disasm::on_Address_textChanged(const QString &arg1) -{ - CtrlDisAsmView *ptr = ui->DisasmView; - ptr->gotoAddr(parseHex(ui->Address->text().toStdString().c_str())); - UpdateDialog(); -} - -void Debugger_Disasm::on_DisasmView_customContextMenuRequested(const QPoint &pos) -{ - ui->DisasmView->contextMenu(pos); -} - -void Debugger_Disasm::NotifyMapLoaded() -{ - FillFunctions(); - CtrlDisAsmView *ptr = ui->DisasmView; - ptr->redraw(); -} - -void Debugger_Disasm::on_RegList_customContextMenuRequested(const QPoint &pos) -{ - ui->RegList->contextMenu(pos); -} - -void Debugger_Disasm::ShowMemory(u32 addr) -{ - mainWindow->ShowMemory(addr); -} - -void Debugger_Disasm::on_FuncList_itemClicked(QListWidgetItem *item) -{ - u32 addr = item->data(Qt::UserRole).toInt(); - - ui->DisasmView->gotoAddr(addr); -} - -void Debugger_Disasm::FillFunctions() -{ - QListWidgetItem* item = new QListWidgetItem(); - item->setText("(0x02000000)"); - item->setData(Qt::UserRole, 0x02000000); - ui->FuncList->addItem(item); - - std::vector symbols = g_symbolMap->GetAllSymbols(ST_FUNCTION); - for(int i = 0; i < (int)symbols.size(); i++) - { - QListWidgetItem* item = new QListWidgetItem(); - item->setText(QString("%1 (%2)").arg(QString::fromStdString(symbols[i].name)).arg(symbols[i].size)); - item->setData(Qt::UserRole, symbols[i].address); - ui->FuncList->addItem(item); - } -} - -void Debugger_Disasm::UpdateCallstack() -{ - emit UpdateCallstack_(); -} - -void Debugger_Disasm::UpdateCallstackGUI() -{ - - auto threads = GetThreadsInfo(); - - u32 entry = 0, stackTop = 0; - for (size_t i = 0; i < threads.size(); i++) - { - if (threads[i].isCurrent) - { - entry = threads[i].entrypoint; - stackTop = threads[i].initialStack; - break; - } - } - if (entry != 0) { - stackTraceModel = MIPSStackWalk::Walk( - cpu->GetPC(), - cpu->GetRegValue(0,MIPS_REG_RA), - cpu->GetRegValue(0,MIPS_REG_SP), - entry, - stackTop); - } else { - stackTraceModel.clear(); - } - - ui->callStack->clear(); - - QTreeWidgetItem* item; - for(auto it=stackTraceModel.begin();it!=stackTraceModel.end();it++) - { - item = new QTreeWidgetItem(); - item->setText(0,QString("%1").arg(it->pc,8,16,QChar('0')).prepend("0x")); - item->setData(0,Qt::UserRole,it->pc); - item->setText(1,QString("%1").arg(it->entry,8,16,QChar('0')).prepend("0x")); - item->setData(1,Qt::UserRole,it->entry); - item->setText(2,QString("%1").arg(it->sp,8,16,QChar('0')).prepend("0x")); - item->setText(3,QString("%1").arg(it->stackSize,8,16,QChar('0')).prepend("0x")); - ui->callStack->addTopLevelItem(item); - } -} - -void Debugger_Disasm::UpdateBreakpoints() -{ - emit UpdateBreakpoints_(); -} - -void Debugger_Disasm::UpdateBreakpointsGUI() -{ - u32 curBpAddr = 0; - QTreeWidgetItem* curItem = ui->breakpointsList->currentItem(); - if(curItem) - curBpAddr = ui->breakpointsList->currentItem()->data(0,Qt::UserRole).toInt(); - - ui->breakpointsList->clear(); - - auto breakpoints = CBreakPoints::GetBreakpoints(); - for(size_t i = 0; i < breakpoints.size(); i++) - { - u32 addr_ = breakpoints[i].addr; - if(!CBreakPoints::IsTempBreakPoint(addr_)) - { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString("%1").arg(addr_,8,16,QChar('0'))); - item->setData(0,Qt::UserRole,addr_); - ui->breakpointsList->addTopLevelItem(item); - if(curBpAddr == addr_) - ui->breakpointsList->setCurrentItem(item); - } - } -} - -void Debugger_Disasm::on_breakpointsList_itemClicked(QTreeWidgetItem *item, int column) -{ - ui->DisasmView->gotoAddr(item->data(column,Qt::UserRole).toInt()); -} - -void Debugger_Disasm::on_breakpointsList_customContextMenuRequested(const QPoint &pos) -{ - QTreeWidgetItem* item = ui->breakpointsList->itemAt(pos); - if(item) - { - breakpointAddr = item->data(0,Qt::UserRole).toInt(); - - QMenu menu(this); - - QAction *removeBP = new QAction(tr("Remove breakpoint"), this); - connect(removeBP, SIGNAL(triggered()), this, SLOT(RemoveBreakpoint())); - menu.addAction(removeBP); - - menu.exec( ui->breakpointsList->mapToGlobal(pos)); - } -} - -void Debugger_Disasm::RemoveBreakpoint() -{ - CBreakPoints::RemoveBreakPoint(breakpointAddr); - Update(); -} - -void Debugger_Disasm::on_clearAllBP_clicked() -{ - CBreakPoints::ClearAllBreakPoints(); - Update(); -} - -void Debugger_Disasm::UpdateThread() -{ - emit UpdateThread_(); -} - -void Debugger_Disasm::UpdateThreadGUI() -{ - ui->threadList->clear(); - - std::vector threads = GetThreadsInfo(); - - for(size_t i = 0; i < threads.size(); i++) - { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString::number(threads[i].id)); - item->setData(0,Qt::UserRole,threads[i].id); - item->setText(1,threads[i].name); - QString status = ""; - if(threads[i].status & THREADSTATUS_RUNNING) status += "Running "; - if(threads[i].status & THREADSTATUS_WAIT) status += "Wait "; - if(threads[i].status & THREADSTATUS_READY) status += "Ready "; - if(threads[i].status & THREADSTATUS_SUSPEND) status += "Suspend "; - if(threads[i].status & THREADSTATUS_DORMANT) status += "Dormant "; - if(threads[i].status & THREADSTATUS_DEAD) status += "Dead "; - item->setText(2,status); - item->setText(3,QString("%1").arg(threads[i].curPC,8,16,QChar('0'))); - item->setData(3,Qt::UserRole,threads[i].curPC); - item->setText(4,QString("%1").arg(threads[i].entrypoint,8,16,QChar('0'))); - item->setData(4,Qt::UserRole,threads[i].entrypoint); - - if(threads[i].isCurrent) - { - for(int j = 0; j < 5; j++) - item->setTextColor(j,Qt::green); - } - - ui->threadList->addTopLevelItem(item); - } - for(int i = 0; i < ui->threadList->columnCount(); i++) - ui->threadList->resizeColumnToContents(i); -} - -void Debugger_Disasm::on_threadList_itemClicked(QTreeWidgetItem *item, int column) -{ - ui->DisasmView->gotoAddr(item->data(3,Qt::UserRole).toInt()); -} - -void Debugger_Disasm::on_threadList_customContextMenuRequested(const QPoint &pos) -{ - QTreeWidgetItem* item = ui->threadList->itemAt(pos); - if(item) - { - threadRowSelected = item; - - QMenu menu(this); - - QAction *gotoEntryPoint = new QAction(tr("Go to entry point"), this); - connect(gotoEntryPoint, SIGNAL(triggered()), this, SLOT(GotoThreadEntryPoint())); - menu.addAction(gotoEntryPoint); - - QMenu* changeStatus = menu.addMenu(tr("Change status")); - - QAction *statusRunning = new QAction(tr("Running"), this); - connect(statusRunning, SIGNAL(triggered()), this, SLOT(SetThreadStatusRun())); - changeStatus->addAction(statusRunning); - - QAction *statusWait = new QAction(tr("Wait"), this); - connect(statusWait, SIGNAL(triggered()), this, SLOT(SetThreadStatusWait())); - changeStatus->addAction(statusWait); - - QAction *statusSuspend = new QAction(tr("Suspend"), this); - connect(statusSuspend, SIGNAL(triggered()), this, SLOT(SetThreadStatusSuspend())); - changeStatus->addAction(statusSuspend); - - menu.exec( ui->threadList->mapToGlobal(pos)); - } -} - -void Debugger_Disasm::GotoThreadEntryPoint() -{ - ui->DisasmView->gotoAddr(threadRowSelected->data(4,Qt::UserRole).toInt()); - Update(); -} - -void Debugger_Disasm::SetThreadStatus(ThreadStatus status) -{ - __KernelChangeThreadState(threadRowSelected->data(0,Qt::UserRole).toInt(), status); - - UpdateThread(); -} - -void Debugger_Disasm::SetThreadStatusRun() -{ - SetThreadStatus(THREADSTATUS_RUNNING); -} - -void Debugger_Disasm::SetThreadStatusWait() -{ - SetThreadStatus(THREADSTATUS_WAIT); -} - -void Debugger_Disasm::SetThreadStatusSuspend() -{ - SetThreadStatus(THREADSTATUS_SUSPEND); -} - -void Debugger_Disasm::UpdateDisplayList() -{ - emit UpdateDisplayList_(); -} - -void Debugger_Disasm::UpdateDisplayListGUI() -{ - u32 curDlId = 0; - QTreeWidgetItem* curItem = ui->displayList->currentItem(); - if(curItem) - curDlId = ui->displayList->currentItem()->data(0,Qt::UserRole).toInt(); - - ui->displayList->clear(); - - const std::list& dlQueue = gpu->GetDisplayLists(); - - DisplayList dlist; - DisplayList* dl = &dlist; - if(gpuDebug->GetCurrentDisplayList(dlist)) - { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString::number(dl->id)); - item->setData(0, Qt::UserRole, dl->id); - switch(dl->state) - { - case PSP_GE_DL_STATE_NONE: item->setText(1,"None"); break; - case PSP_GE_DL_STATE_QUEUED: item->setText(1,"Queued"); break; - case PSP_GE_DL_STATE_RUNNING: item->setText(1,"Running"); break; - case PSP_GE_DL_STATE_COMPLETED: item->setText(1,"Completed"); break; - case PSP_GE_DL_STATE_PAUSED: item->setText(1,"Paused"); break; - default: break; - } - item->setText(2,QString("%1").arg(dl->startpc,8,16,QChar('0'))); - item->setData(2, Qt::UserRole, dl->startpc); - item->setText(3,QString("%1").arg(dl->pc,8,16,QChar('0'))); - item->setData(3, Qt::UserRole, dl->pc); - ui->displayList->addTopLevelItem(item); - if(curDlId == (u32)dl->id) - { - ui->displayList->setCurrentItem(item); - displayListRowSelected = item; - } - } - - for(auto listIdIt = dlQueue.begin(); listIdIt != dlQueue.end(); ++listIdIt) - { - DisplayList *it = gpu->getList(*listIdIt); - if(dl && it->id == dl->id) - continue; - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString::number(it->id)); - item->setData(0, Qt::UserRole, it->id); - switch(it->state) - { - case PSP_GE_DL_STATE_NONE: item->setText(1,"None"); break; - case PSP_GE_DL_STATE_QUEUED: item->setText(1,"Queued"); break; - case PSP_GE_DL_STATE_RUNNING: item->setText(1,"Running"); break; - case PSP_GE_DL_STATE_COMPLETED: item->setText(1,"Completed"); break; - case PSP_GE_DL_STATE_PAUSED: item->setText(1,"Paused"); break; - default: break; - } - item->setText(2,QString("%1").arg(it->startpc,8,16,QChar('0'))); - item->setData(2, Qt::UserRole, it->startpc); - item->setText(3,QString("%1").arg(it->pc,8,16,QChar('0'))); - item->setData(3, Qt::UserRole, it->pc); - ui->displayList->addTopLevelItem(item); - if(curDlId == (u32)it->id) - { - ui->displayList->setCurrentItem(item); - displayListRowSelected = item; - } - } - for(int i = 0; i < ui->displayList->columnCount(); i++) - ui->displayList->resizeColumnToContents(i); -} - -void Debugger_Disasm::on_displayList_customContextMenuRequested(const QPoint &pos) -{ - QTreeWidgetItem* item = ui->displayList->itemAt(pos); - if(item) - { - displayListRowSelected = item; - - /*QMenu menu(this); - - QAction *showCode = new QAction(tr("Show code"), this); - connect(showCode, SIGNAL(triggered()), this, SLOT(ShowDLCode())); - menu.addAction(showCode);*/ - - //menu.exec( ui->displayList->mapToGlobal(pos)); - } -} diff --git a/Qt/Debugger/debugger_disasm.h b/Qt/Debugger/debugger_disasm.h deleted file mode 100644 index d2fa1d0a54..0000000000 --- a/Qt/Debugger/debugger_disasm.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef DEBUGGER_DISASM_H -#define DEBUGGER_DISASM_H - -#include "Core/MIPS/MIPSStackWalk.h" -#include "Core/HLE/sceKernelThread.h" -#include "Core/Debugger/DebugInterface.h" -#include -#include -#include -#include - -class MainWindow; -namespace Ui { -class Debugger_Disasm; -} - -class Debugger_Disasm : public QDialog -{ - Q_OBJECT - -public: - explicit Debugger_Disasm(DebugInterface *_cpu, MainWindow* mainWindow_, QWidget *parent = 0); - ~Debugger_Disasm(); - void SetDebugMode(bool _bDebug); - - void Go(); - void Step(); - void StepOver(); - void StepHLE(); - void Stop(); - void Skip(); - void GotoPC(); - void GotoLR(); - void UpdateDialog(); - void NotifyMapLoaded(); - void Update(); - void ShowMemory(u32 addr); - void FillFunctions(); - void UpdateCallstack(); - void UpdateBreakpoints(); - void UpdateThread(); - void UpdateDisplayList(); - -signals: - void UpdateCallstack_(); - void UpdateDisplayList_(); - void UpdateBreakpoints_(); - void UpdateThread_(); - -public slots: - void Goto(u32 addr); - void RemoveBreakpoint(); - void GotoThreadEntryPoint(); - -private slots: - void UpdateCallstackGUI(); - void UpdateDisplayListGUI(); - void UpdateBreakpointsGUI(); - void UpdateThreadGUI(); - - void on_GotoPc_clicked(); - void on_Go_clicked(); - void on_Stop_clicked(); - void on_StepInto_clicked(); - void on_StepOver_clicked(); - void on_Skip_clicked(); - void on_NextHLE_clicked(); - void on_GotoLr_clicked(); - void on_GotoInt_currentIndexChanged(int index); - void on_Address_textChanged(const QString &arg1); - void on_DisasmView_customContextMenuRequested(const QPoint &pos); - - void on_RegList_customContextMenuRequested(const QPoint &pos); - void on_FuncList_itemClicked(QListWidgetItem *item); - void on_breakpointsList_itemClicked(QTreeWidgetItem *item, int column); - void on_breakpointsList_customContextMenuRequested(const QPoint &pos); - void on_clearAllBP_clicked(); - void on_threadList_itemClicked(QTreeWidgetItem *item, int column); - void on_threadList_customContextMenuRequested(const QPoint &pos); - - void SetThreadStatusRun(); - void SetThreadStatusWait(); - void SetThreadStatusSuspend(); - void on_displayList_customContextMenuRequested(const QPoint &pos); - -private: - void SetThreadStatus(ThreadStatus status); - - Ui::Debugger_Disasm *ui; - DebugInterface* cpu; - MainWindow* mainWindow; - u32 breakpointAddr; - QTreeWidgetItem* threadRowSelected; - QTreeWidgetItem* displayListRowSelected; - std::vector stackTraceModel; -}; - -#endif // DEBUGGER_DISASM_H diff --git a/Qt/Debugger/debugger_disasm.ui b/Qt/Debugger/debugger_disasm.ui deleted file mode 100644 index 89f5783e78..0000000000 --- a/Qt/Debugger/debugger_disasm.ui +++ /dev/null @@ -1,523 +0,0 @@ - - - Debugger_Disasm - - - - 0 - 0 - 837 - 492 - - - - Qt::ClickFocus - - - Disassembler - - - - - - Qt::Horizontal - - - - - - - Ctr: - - - - - - - - 0 - 0 - - - - &Go to - - - - 0 - - - 4 - - - 4 - - - 0 - - - 0 - - - - - - - - QLayout::SetDefaultConstraint - - - - - - 0 - 0 - - - - &PC - - - - - - - - 0 - 0 - - - - &LR - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - 0 - 0 - - - - Regs - - - - - - - 0 - 0 - - - - Qt::ClickFocus - - - Qt::CustomContextMenu - - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 120 - 20 - - - - - - - - Qt::Vertical - - - - - - - - - - - Funcs - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - &Go - - - - - - - - 0 - 0 - - - - Stop - - - - - - - - 0 - 0 - - - - Step &Into - - - - - - - false - - - - 0 - 0 - - - - Step &Over - - - - - - - - 0 - 0 - - - - S&kip - - - - - - - - 0 - 0 - - - - Next &HLE - - - - - - - - - - 0 - 0 - - - - Qt::ClickFocus - - - Qt::CustomContextMenu - - - - - - - 0 - - - - Breakpoints - - - - - - Qt::CustomContextMenu - - - false - - - false - - - 30 - - - - Address - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Clear All - - - - - - - - - - Callstack - - - - - - 4 - - - - Address - - - - - Entry Point - - - - - SP - - - - - length - - - - - - - - - Display Lists - - - - - - Qt::Horizontal - - - - Qt::CustomContextMenu - - - false - - - false - - - false - - - 30 - - - - Id - - - - - Status - - - - - Start Address - - - - - Current Address - - - - - - - - - - Threads - - - - - - Qt::CustomContextMenu - - - false - - - false - - - false - - - 50 - - - - Id - - - - - Name - - - - - Status - - - - - Current PC - - - - - Entry point - - - - - - - - - - - - - - - - - CtrlDisAsmView - QWidget -
ctrldisasmview.h
- 1 -
- - CtrlRegisterList - QWidget -
ctrlregisterlist.h
- 1 -
-
- - -
diff --git a/Qt/QtHost.h b/Qt/QtHost.h index 2df6155bb0..5a0f201b10 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -41,15 +41,12 @@ public: mainWindow->GetDialogMemory()->Update(); } virtual void UpdateDisassembly() override { - if(mainWindow->GetDialogDisasm()) - mainWindow->GetDialogDisasm()->Update(); + mainWindow->updateMenus(); if(mainWindow->GetDialogDisplaylist()) mainWindow->GetDialogDisplaylist()->Update(); } virtual void SetDebugMode(bool mode) override { - if(mainWindow->GetDialogDisasm()) - mainWindow->GetDialogDisasm()->SetDebugMode(mode); } virtual bool InitGraphics(std::string *error_message, GraphicsContext **ctx) override { return true; } diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 116b7f084a..e5c1ee978f 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -21,7 +21,6 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : currentLanguage("en"), nextState(CORE_POWERDOWN), lastUIState(UISTATE_MENU), - dialogDisasm(0), memoryWindow(0), memoryTexWindow(0), displaylistWindow(0) @@ -145,10 +144,6 @@ void MainWindow::updateMenus() void MainWindow::bootDone() { - dialogDisasm = new Debugger_Disasm(currentDebugMIPS, this, this); - if(g_Config.bShowDebuggerOnLoad) - dialogDisasm->show(); - if(g_Config.bFullScreen != isFullScreen()) fullscrAct(); @@ -177,11 +172,8 @@ void MainWindow::openAct() void MainWindow::closeAct() { - if(dialogDisasm) - dialogDisasm->Stop(); + updateMenus(); - if(dialogDisasm && dialogDisasm->isVisible()) - dialogDisasm->close(); if(memoryWindow && memoryWindow->isVisible()) memoryWindow->close(); if(memoryTexWindow && memoryTexWindow->isVisible()) @@ -266,11 +258,8 @@ void MainWindow::pauseAct() void MainWindow::resetAct() { - if(dialogDisasm) - dialogDisasm->Stop(); + updateMenus(); - if(dialogDisasm) - dialogDisasm->close(); if(memoryWindow) memoryWindow->close(); if(memoryTexWindow) @@ -333,12 +322,6 @@ void MainWindow::dumpNextAct() gpu->DumpNextFrame(); } -void MainWindow::disasmAct() -{ - if(dialogDisasm) - dialogDisasm->show(); -} - void MainWindow::dpyListAct() { if(displaylistWindow) @@ -537,8 +520,6 @@ void MainWindow::createMenus() debugMenu->add(new MenuAction(this, SLOT(takeScreen()), QT_TR_NOOP("Take Screenshot"), Qt::Key_F12)) ->addDisableState(UISTATE_MENU); debugMenu->addSeparator(); - debugMenu->add(new MenuAction(this, SLOT(disasmAct()), QT_TR_NOOP("Disassembly"), Qt::CTRL + Qt::Key_D)) - ->addDisableState(UISTATE_MENU); //commented out until someone bothers to maintain it //debugMenu->add(new MenuAction(this, SLOT(dpyListAct()), QT_TR_NOOP("Display List..."))) // ->addDisableState(UISTATE_MENU); @@ -671,8 +652,6 @@ void MainWindow::createMenus() void MainWindow::notifyMapsLoaded() { - if (dialogDisasm) - dialogDisasm->NotifyMapLoaded(); if (memoryWindow) memoryWindow->NotifyMapLoaded(); } diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index ba2d3ff7dc..4f702bfc87 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -12,7 +12,6 @@ #include "Core/Core.h" #include "Core/Config.h" #include "Core/System.h" -#include "Debugger/debugger_disasm.h" #include "Debugger/debugger_memory.h" #include "Debugger/debugger_memorytex.h" #include "Debugger/debugger_displaylist.h" @@ -37,7 +36,6 @@ public: explicit MainWindow(QWidget *parent = 0, bool fullscreen=false); ~MainWindow() { }; - Debugger_Disasm* GetDialogDisasm() { return dialogDisasm; } Debugger_Memory* GetDialogMemory() { return memoryWindow; } Debugger_MemoryTex* GetDialogMemoryTex() { return memoryTexWindow; } Debugger_DisplayList* GetDialogDisplaylist() { return displaylistWindow; } @@ -97,7 +95,6 @@ private slots: void resetTableAct(); void dumpNextAct(); void takeScreen() { g_TakeScreenshot = true; } - void disasmAct(); void dpyListAct(); void consoleAct(); void memviewAct(); @@ -167,7 +164,6 @@ private: CoreState nextState; GlobalUIState lastUIState; - Debugger_Disasm *dialogDisasm; Debugger_Memory *memoryWindow; Debugger_MemoryTex *memoryTexWindow; Debugger_DisplayList *displaylistWindow; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 9e95c6a1b8..1020d0b76b 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1,4 +1,4 @@ - + @@ -341,36 +341,12 @@ - - true - true - true - true - true true true true - - true - true - true - true - - - true - true - true - true - - - true - true - true - true - true true @@ -389,12 +365,6 @@ true true - - true - true - true - true - true true @@ -494,15 +464,10 @@ - - - - - @@ -562,11 +527,9 @@ - - @@ -627,4 +590,4 @@ - \ No newline at end of file + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 8cce6b63ec..9e263f79d9 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -161,9 +161,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger @@ -173,21 +170,9 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - - - Other Platforms\Qt\Debugger - Windows\System @@ -341,9 +326,6 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger @@ -353,18 +335,9 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Windows\System @@ -468,9 +441,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger @@ -480,9 +450,6 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - @@ -494,4 +461,4 @@ Resource Files - \ No newline at end of file + From 28cabd578a79bc5345cb7ed50d4b21aea682f829 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 11:09:18 -0700 Subject: [PATCH 114/301] Qt Debugger: Remove display list debugger. Being replaced with WebSocket based debugger. Was disabled and not working anyway, and hardcoded some GL so a barrier to Vulkan. --- CMakeLists.txt | 3 - GPU/GPUInterface.h | 1 + Qt/Debugger/debugger_displaylist.cpp | 1666 -------------------------- Qt/Debugger/debugger_displaylist.h | 104 -- Qt/Debugger/debugger_displaylist.ui | 533 -------- Qt/QtHost.h | 19 - Qt/mainwindow.cpp | 17 +- Qt/mainwindow.h | 4 - Windows/PPSSPP.vcxproj | 8 - Windows/PPSSPP.vcxproj.filters | 9 - 10 files changed, 2 insertions(+), 2362 deletions(-) delete mode 100644 Qt/Debugger/debugger_displaylist.cpp delete mode 100644 Qt/Debugger/debugger_displaylist.h delete mode 100644 Qt/Debugger/debugger_displaylist.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 48d9ae691a..187fc2e21c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -757,7 +757,6 @@ elseif(USING_QT_UI) find_package(Qt5 COMPONENTS Multimedia) endif(NOT SDL2_FOUND) set(Qt_UI - Qt/Debugger/debugger_displaylist.ui Qt/Debugger/debugger_memory.ui Qt/Debugger/debugger_memorytex.ui ) @@ -772,8 +771,6 @@ elseif(USING_QT_UI) Qt/mainwindow.h Qt/Debugger/ctrlmemview.cpp Qt/Debugger/ctrlmemview.h - Qt/Debugger/debugger_displaylist.cpp - Qt/Debugger/debugger_displaylist.h Qt/Debugger/debugger_memory.cpp Qt/Debugger/debugger_memory.h Qt/Debugger/debugger_memorytex.cpp diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 101f73e14b..543f757a99 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -241,6 +241,7 @@ public: virtual void DumpNextFrame() = 0; virtual void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) = 0; virtual const std::list& GetDisplayLists() = 0; + // TODO: Currently Qt only, needs to be cleaned up. virtual std::vector GetFramebufferList() = 0; virtual s64 GetListTicks(int listid) = 0; diff --git a/Qt/Debugger/debugger_displaylist.cpp b/Qt/Debugger/debugger_displaylist.cpp deleted file mode 100644 index 6089c64003..0000000000 --- a/Qt/Debugger/debugger_displaylist.cpp +++ /dev/null @@ -1,1666 +0,0 @@ -#include "debugger_displaylist.h" - -#include -#include -#include - -#include "ui_debugger_displaylist.h" -#include "GPU/GPUInterface.h" -#include "GPU/GeDisasm.h" -#include "Core/Host.h" -#include "base/display.h" -#include "mainwindow.h" -#include "Qt/QtHost.h" - -//commented out until someone bothers to maintain it (see below) -//#include "GPU/GLES/VertexDecoder.h" - -Debugger_DisplayList::Debugger_DisplayList(DebugInterface *_cpu, Draw::DrawContext *draw, MainWindow* mainWindow_, QWidget *parent) : - QDialog(parent), - ui(new Ui::Debugger_DisplayList), - cpu(_cpu), - draw_(draw), - mainWindow(mainWindow_), - currentRenderFrameDisplay(0), - currentTextureDisplay(0), - fboZoomFactor(1), - maxVtxDisplay(20), - maxIdxDisplay(20) -{ - ui->setupUi(this); - - QObject::connect(this, SIGNAL(updateDisplayList_()), this, SLOT(UpdateDisplayListGUI())); - QObject::connect(this, SIGNAL(updateRenderBufferList_()), this, SLOT(UpdateRenderBufferListGUI())); - QObject::connect(this, SIGNAL(updateRenderBuffer_()), this, SLOT(UpdateRenderBufferGUI())); - -} - -Debugger_DisplayList::~Debugger_DisplayList() -{ - delete ui; -} - -void Debugger_DisplayList::UpdateDisplayList() -{ - emit updateDisplayList_(); -} - - -void Debugger_DisplayList::UpdateDisplayListGUI() -{ - u32 curDlId = 0; - QTreeWidgetItem* curItem = ui->displayList->currentItem(); - if(curItem) - curDlId = ui->displayList->currentItem()->data(0,Qt::UserRole).toInt(); - - displayListRowSelected = 0; - ui->displayList->clear(); - ui->displayListData->clear(); - - const std::list& dlQueue = gpu->GetDisplayLists(); - - for(auto listIdIt = dlQueue.begin(); listIdIt != dlQueue.end(); ++listIdIt) - { - DisplayList *it = gpu->getList(*listIdIt); - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString::number(it->id)); - item->setData(0, Qt::UserRole, it->id); - switch(it->state) - { - case PSP_GE_DL_STATE_NONE: item->setText(1,"None"); break; - case PSP_GE_DL_STATE_QUEUED: item->setText(1,"Queued"); break; - case PSP_GE_DL_STATE_RUNNING: item->setText(1,"Running"); break; - case PSP_GE_DL_STATE_COMPLETED: item->setText(1,"Completed"); break; - case PSP_GE_DL_STATE_PAUSED: item->setText(1,"Paused"); break; - default: break; - } - item->setText(2,QString("%1").arg(it->startpc,8,16,QChar('0'))); - item->setData(2, Qt::UserRole, it->startpc); - item->setText(3,QString("%1").arg(it->pc,8,16,QChar('0'))); - item->setData(3, Qt::UserRole, it->pc); - ui->displayList->addTopLevelItem(item); - if(curDlId == (u32)it->id) - { - ui->displayList->setCurrentItem(item); - displayListRowSelected = item; - ShowDLCode(); - } - } - for(int i = 0; i < ui->displayList->columnCount(); i++) - ui->displayList->resizeColumnToContents(i); - - if (ui->displayList->selectedItems().size() == 0 && ui->displayList->topLevelItemCount() != 0) - { - ui->displayList->setCurrentItem(ui->displayList->topLevelItem(0)); - displayListRowSelected = ui->displayList->topLevelItem(0); - ShowDLCode(); - } -} - - -void Debugger_DisplayList::ShowDLCode() -{ - ui->displayListData->clear(); - ui->displayListData->setColumnCount(4); - - u32 startPc = displayListRowSelected->data(2,Qt::UserRole).toInt(); - u32 curPc = displayListRowSelected->data(3,Qt::UserRole).toInt(); - - std::map data; - GPUgstate listState; - memset(&listState,0,sizeof(GPUgstate)); - drawGPUState.clear(); - vtxBufferSize.clear(); - idxBufferSize.clear(); - - FillDisplayListCmd(data, startPc,0, listState); - - u32 curTexAddr = 0; - u32 curVtxAddr = 0; - u32 curIdxAddr = 0; - - for(std::map::iterator it = data.begin(); it != data.end(); it++) - { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString("%1").arg(it->first,8,16,QChar('0'))); - item->setData(0, Qt::UserRole, it->first); - item->setText(1,QString("%1").arg(it->second.cmd,2,16,QChar('0'))); - item->setText(2,QString("%1").arg(it->second.data,6,16,QChar('0'))); - item->setText(3,it->second.comment); - if(curPc == (u32)it->first) - { - curTexAddr = it->second.texAddr; - curVtxAddr = it->second.vtxAddr; - curIdxAddr = it->second.idxAddr; - setCurrentFBO(it->second.fboAddr); - for(int j = 0; j < ui->displayListData->columnCount(); j++) - item->setTextColor(j, Qt::green); - } - if(it->second.implementationNotFinished) - { - for(int j = 0; j < ui->displayListData->columnCount(); j++) - item->setBackgroundColor(j, Qt::red); - } - ui->displayListData->addTopLevelItem(item); - - if(curPc == (u32)it->first) - { - ui->displayListData->setCurrentItem(item); - } - } - for(int j = 0; j < ui->displayListData->columnCount(); j++) - ui->displayListData->resizeColumnToContents(j); - - ui->texturesList->clear(); - ui->vertexData->clear(); - ui->vertexList->clear(); - ui->indexData->clear(); - ui->indexList->clear(); - - std::set usedTexAddr; - std::set usedVtxAddr; - std::set usedIdxAddr; - for(int i = 0; i < (int)drawGPUState.size(); i++) - { - // Textures - QTreeWidgetItem* item = new QTreeWidgetItem(); - u32 texaddr = (drawGPUState[i].texaddr[0] & 0xFFFFF0) | ((drawGPUState[i].texbufwidth[0]<<8) & 0x0F000000); - if(usedTexAddr.find(texaddr) == usedTexAddr.end() && Memory::IsValidAddress(texaddr)) - { - u32 format = drawGPUState[i].texformat & 0xF; - int w = 1 << (drawGPUState[i].texsize[0] & 0xf); - int h = 1 << ((drawGPUState[i].texsize[0]>>8) & 0xf); - - item->setText(0,QString("%1").arg(texaddr,8,16,QChar('0'))); - item->setData(0, Qt::UserRole, i); - item->setText(1,QString::number(w)); - item->setText(2,QString::number(h)); - item->setText(3,QString::number(format,16)); - ui->texturesList->addTopLevelItem(item); - if(curTexAddr == texaddr) - { - ui->texturesList->setCurrentItem(item); - for(int j = 0; j < ui->texturesList->columnCount(); j++) - item->setTextColor(j,Qt::green); - } - usedTexAddr.insert(texaddr); - } - - // Vertex - QTreeWidgetItem* vertexItem = new QTreeWidgetItem(); - u32 baseExtended = ((drawGPUState[i].base & 0x0F0000) << 8) | (drawGPUState[i].vaddr & 0xFFFFFF); - u32 vaddr = ((drawGPUState[i].offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - if(drawGPUState[i].vaddr != 0 && Memory::IsValidAddress(vaddr) && usedVtxAddr.find(vaddr) == usedVtxAddr.end()) - { - vertexItem->setText(0, QString("%1").arg(vaddr,8,16,QChar('0'))); - vertexItem->setData(0,Qt::UserRole, i); - if((drawGPUState[i].vertType & GE_VTYPE_THROUGH_MASK) == GE_VTYPE_TRANSFORM) - vertexItem->setText(1, "Transform"); - else - vertexItem->setText(1, "Raw"); - vertexItem->setText(2, QString::number((drawGPUState[i].vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT)); - vertexItem->setText(3, QString::number((drawGPUState[i].vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT)); - switch(drawGPUState[i].vertType & GE_VTYPE_WEIGHT_MASK) - { - case GE_VTYPE_WEIGHT_8BIT: vertexItem->setText(4, "8bit"); break; - case GE_VTYPE_WEIGHT_16BIT: vertexItem->setText(4, "16bit"); break; - case GE_VTYPE_WEIGHT_FLOAT: vertexItem->setText(4, "float"); break; - default: vertexItem->setText(4, "No"); break; - } - switch(drawGPUState[i].vertType & GE_VTYPE_POS_MASK) - { - case GE_VTYPE_POS_8BIT: vertexItem->setText(5, "8bit"); break; - case GE_VTYPE_POS_16BIT: vertexItem->setText(5, "16bit"); break; - case GE_VTYPE_POS_FLOAT: vertexItem->setText(5, "float"); break; - default: vertexItem->setText(5, "No"); break; - } - switch(drawGPUState[i].vertType & GE_VTYPE_NRM_MASK) - { - case GE_VTYPE_NRM_8BIT: vertexItem->setText(6, "8bit"); break; - case GE_VTYPE_NRM_16BIT: vertexItem->setText(6, "16bit"); break; - case GE_VTYPE_NRM_FLOAT: vertexItem->setText(6, "float"); break; - default: vertexItem->setText(6, "No"); break; - } - switch(drawGPUState[i].vertType & GE_VTYPE_COL_MASK) - { - case GE_VTYPE_COL_4444: vertexItem->setText(7, "4444"); break; - case GE_VTYPE_COL_5551: vertexItem->setText(7, "5551"); break; - case GE_VTYPE_COL_565: vertexItem->setText(7, "565"); break; - case GE_VTYPE_COL_8888: vertexItem->setText(7, "8888"); break; - default: vertexItem->setText(7, "No"); break; - } - switch(drawGPUState[i].vertType & GE_VTYPE_TC_MASK) - { - case GE_VTYPE_TC_8BIT: vertexItem->setText(8, "8bit"); break; - case GE_VTYPE_TC_16BIT: vertexItem->setText(8, "16bit"); break; - case GE_VTYPE_TC_FLOAT: vertexItem->setText(8, "float"); break; - default: vertexItem->setText(8, "No"); break; - } - - ui->vertexList->addTopLevelItem(vertexItem); - if(curVtxAddr == vaddr) - { - ui->vertexList->setCurrentItem(vertexItem); - for(int j = 0; j < ui->vertexList->columnCount(); j++) - vertexItem->setTextColor(j,Qt::green); - } - usedVtxAddr.insert(vaddr); - } - - - // Index - QTreeWidgetItem* indexItem = new QTreeWidgetItem(); - baseExtended = ((drawGPUState[i].base & 0x0F0000) << 8) | (drawGPUState[i].iaddr & 0xFFFFFF); - u32 iaddr = ((drawGPUState[i].offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - if((drawGPUState[i].iaddr & 0xFFFFFF) != 0 && Memory::IsValidAddress(iaddr) && usedIdxAddr.find(iaddr) == usedIdxAddr.end()) - { - indexItem->setText(0, QString("%1").arg(iaddr,8,16,QChar('0'))); - indexItem->setData(0,Qt::UserRole, i); - - ui->indexList->addTopLevelItem(indexItem); - if(curIdxAddr == iaddr) - { - ui->indexList->setCurrentItem(indexItem); - for(int j = 0; j < ui->indexList->columnCount(); j++) - indexItem->setTextColor(j,Qt::green); - } - usedIdxAddr.insert(iaddr); - } - } - - - for(int i = 0; i < ui->texturesList->columnCount(); i++) - ui->texturesList->resizeColumnToContents(i); - for(int i = 0; i < ui->vertexList->columnCount(); i++) - ui->vertexList->resizeColumnToContents(i); - for(int i = 0; i < ui->indexList->columnCount(); i++) - ui->indexList->resizeColumnToContents(i); - - UpdateVertexInfo(); - UpdateIndexInfo(); -} - - -QString Debugger_DisplayList::DisassembleOp(u32 pc, u32 op, u32 prev, const GPUgstate& state) { - u32 cmd = op >> 24; - u32 data = op & 0xFFFFFF; - - // Handle control and drawing commands here directly. The others we delegate. - switch (cmd) - { - case GE_CMD_BASE: - return QString("BASE: %1").arg(data & 0xFFFFFF,6,16,QChar('0')); - - case GE_CMD_VADDR: /// <<8???? - { - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (data & 0xFFFFFF); - baseExtended = (state.offsetAddr + baseExtended) & 0x0FFFFFFF; - return QString("VADDR: %1").arg(baseExtended,6,16,QChar('0')); - } - - case GE_CMD_IADDR: - { - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (data & 0xFFFFFF); - baseExtended = (state.offsetAddr + baseExtended) & 0x0FFFFFFF; - return QString("IADDR: %1").arg(baseExtended,6,16,QChar('0')); - } - - case GE_CMD_PRIM: - { - u32 count = data & 0xFFFF; - u32 type = data >> 16; - static const char* types[7] = { - "POINTS", - "LINES", - "LINE_STRIP", - "TRIANGLES", - "TRIANGLE_STRIP", - "TRIANGLE_FAN", - "RECTANGLES", - }; - return QString("DrawPrim type: %1 count: %2").arg(type < 7 ? types[type] : "INVALID").arg(count); - } - - // The arrow and other rotary items in Puzbob are bezier patches, strangely enough. - case GE_CMD_BEZIER: - { - int bz_ucount = data & 0xFF; - int bz_vcount = (data >> 8) & 0xFF; - return QString("DRAW BEZIER: U=%1 x V=%2").arg(bz_ucount).arg(bz_vcount); - } - - case GE_CMD_SPLINE: - { - int sp_ucount = data & 0xFF; - int sp_vcount = (data >> 8) & 0xFF; - static const char* type[4] = { - "Close/Close", - "Open/Close", - "Close/Open", - "Open/Open" - }; - int sp_utype = (data >> 16) & 0x3; - int sp_vtype = (data >> 18) & 0x3; - return QString("DRAW SPLINE: U=%1 x V=%2, U Type = %3 , V Type = %4").arg(sp_ucount).arg(sp_vcount).arg(type[sp_utype]).arg(type[sp_vtype]); - } - - case GE_CMD_JUMP: - { - u32 target = (((state.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; - return QString("CMD JUMP - %1 to %2").arg(pc,8,16,QChar('0')).arg(target,8,16,QChar('0')); - } - - case GE_CMD_CALL: - { - u32 retval = pc + 4; - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (op & 0xFFFFFF); - u32 target = (state.offsetAddr + baseExtended) & 0x0FFFFFFF; - return QString("CMD CALL - %1 to %2, ret=%3").arg(pc,8,16,QChar('0')).arg(target,8,16,QChar('0')).arg(retval,8,16,QChar('0')); - } - - case GE_CMD_RET: - return QString("CMD RET"); - - case GE_CMD_SIGNAL: - return QString("GE_CMD_SIGNAL %1").arg(data,6,16,QChar('0')); - - case GE_CMD_FINISH: - return QString("CMD FINISH %1").arg(data,6,16,QChar('0')); - - case GE_CMD_END: - switch (prev >> 24) - { - case GE_CMD_SIGNAL: - { - // TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935# - int behaviour = (prev >> 16) & 0xFF; - int signal = prev & 0xFFFF; - int enddata = data & 0xFFFF; - // We should probably defer to sceGe here, no sense in implementing this stuff in every GPU - switch (behaviour) { - case 1: // Signal with Wait - return QString("Signal with Wait UNIMPLEMENTED! signal/end: %1 %2").arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - case 2: - return QString("Signal without wait. signal/end: %1 %2").arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - case 3: - return QString("Signal with Pause UNIMPLEMENTED! signal/end: %1 %2").arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - case 0x10: - return QString("Signal with Jump UNIMPLEMENTED! signal/end: %1 %2").arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - case 0x11: - return QString("Signal with Call UNIMPLEMENTED! signal/end: %1 %2").arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - case 0x12: - return QString("Signal with Return UNIMPLEMENTED! signal/end: %1 %2").arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - default: - return QString("UNKNOWN Signal UNIMPLEMENTED %1 ! signal/end: %1 %2").arg(behaviour).arg(signal,4,16,QChar('0')).arg(enddata,4,16,QChar('0')); - } - } - break; - case GE_CMD_FINISH: - break; - default: - return QString("Ah, not finished: %1").arg(prev & 0xFFFFFF,6,16,QChar('0')); - } - return "CMD END"; - - case GE_CMD_BJUMP: - { - // bounding box jump. Let's just not jump, for now. - u32 target = (((state.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF; - return QString("BBOX JUMP - %1 to %2").arg(pc,8,16,QChar('0')).arg(target,8,16,QChar('0')); - } - case GE_CMD_BOUNDINGBOX: - // bounding box test. Let's do nothing. - return QString("BBOX TEST - number : %1").arg(data & 0xFFFF,4,16,QChar('0')); - - case GE_CMD_ORIGIN: - return QString("Origin: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_VERTEXTYPE: - { - const char* format[4] = - { - "No", - "8 Bits fixed", - "16 Bits fixed", - "Float" - }; - const char* colFormat[8] = - { - "No", - "", - "", - "", - "16-bit BGR-5650", - "16-bit ABGR-5551", - "16-bit ABGR-4444", - "32-bit ABGR-8888" - }; - QString retString = "SetVertexType:"; - - u32 transform = data & GE_VTYPE_THROUGH_MASK; - retString += QString(" Transform : %1").arg(transform==0?"Transformed":"Raw"); - - u32 numVertMorph = (data & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT; - retString += QString(", Num Vtx Morph : %1").arg(numVertMorph); - - u32 numWeight = (data & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT; - retString += QString(", Num Weights : %1").arg(numWeight); - - u32 indexFmt = (data & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT; - retString += QString(", Index Format : %1").arg(format[indexFmt]); - - u32 weightFmt = (data & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT; - retString += QString(", Weight Format : %1").arg(format[weightFmt]); - - u32 posFmt = (data & GE_VTYPE_POS_MASK) >> GE_VTYPE_POS_SHIFT; - retString += QString(", Position Format : %1").arg(format[posFmt]); - - u32 nrmFmt = (data & GE_VTYPE_NRM_MASK) >> GE_VTYPE_NRM_SHIFT; - retString += QString(", Normal Format : %1").arg(format[nrmFmt]); - - u32 colFmt = (data & GE_VTYPE_COL_MASK) >> GE_VTYPE_COL_SHIFT; - retString += QString(", Color Format : %1").arg(colFormat[colFmt]); - - u32 tcFmt = (data & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT; - retString += QString(", Texture UV Format : %1").arg(format[tcFmt]); - - return retString; - } - case GE_CMD_OFFSETADDR: - return QString("OffsetAddr: %1").arg(data,6,16,QChar('0')); - break; - - case GE_CMD_REGION1: - { - int x1 = data & 0x3ff; - int y1 = data >> 10; - //topleft - return QString("Region TL: %1 %2").arg(x1).arg(y1); - } - - case GE_CMD_REGION2: - { - int x2 = data & 0x3ff; - int y2 = data >> 10; - return QString("Region BR: %1 %2").arg(x2).arg(y2); - } - - case GE_CMD_CLIPENABLE: - return QString("Clip Enable: %1").arg(data); - - case GE_CMD_CULLFACEENABLE: - return QString("CullFace Enable: %1").arg(data); - - case GE_CMD_TEXTUREMAPENABLE: - return QString("Texture map enable: %1").arg(data); - - case GE_CMD_LIGHTINGENABLE: - return QString("Lighting enable: %1").arg(data); - - case GE_CMD_FOGENABLE: - return QString("Fog Enable: %1").arg(data); - - case GE_CMD_DITHERENABLE: - return QString("Dither Enable: %1").arg(data); - - case GE_CMD_OFFSETX: - return QString("Offset X: %1").arg(data); - - case GE_CMD_OFFSETY: - return QString("Offset Y: %1").arg(data); - - case GE_CMD_TEXSCALEU: - return QString("Texture U Scale: %1").arg(getFloat24(data)); - - case GE_CMD_TEXSCALEV: - return QString("Texture V Scale: %1").arg(getFloat24(data)); - - case GE_CMD_TEXOFFSETU: - return QString("Texture U Offset: %1").arg(getFloat24(data)); - - case GE_CMD_TEXOFFSETV: - return QString("Texture V Offset: %1").arg(getFloat24(data)); - - case GE_CMD_SCISSOR1: - { - int x1 = data & 0x3ff; - int y1 = data >> 10; - return QString("Scissor TL: %1, %2").arg(x1).arg(y1); - } - case GE_CMD_SCISSOR2: - { - int x2 = data & 0x3ff; - int y2 = data >> 10; - return QString("Scissor BR: %1, %2").arg(x2).arg(y2); - } - - case GE_CMD_MINZ: - { - float zMin = getFloat24(data) / 65535.f; - return QString("MinZ: %1").arg(zMin); - } - - case GE_CMD_MAXZ: - { - float zMax = getFloat24(data) / 65535.f; - return QString("MaxZ: %1").arg(zMax); - } - - case GE_CMD_FRAMEBUFPTR: - { - return QString("FramebufPtr: %1").arg(data,8,16,QChar('0')); - } - - case GE_CMD_FRAMEBUFWIDTH: - { - return QString("FramebufWidth: %1").arg(data); - } - - case GE_CMD_FRAMEBUFPIXFORMAT: - { - const char* fmt[4] = - { - "16-bit BGR 5650", - "16-bit ABGR 5551", - "16-bit ABGR 4444", - "32-bit ABGR 8888" - }; - return QString("FramebufPixeFormat: %1").arg(fmt[data]); - } - case GE_CMD_TEXADDR0: - case GE_CMD_TEXADDR1: - case GE_CMD_TEXADDR2: - case GE_CMD_TEXADDR3: - case GE_CMD_TEXADDR4: - case GE_CMD_TEXADDR5: - case GE_CMD_TEXADDR6: - case GE_CMD_TEXADDR7: - return QString("Texture address %1: %2").arg(cmd-GE_CMD_TEXADDR0).arg(data,6,16,QChar('0')); - - case GE_CMD_TEXBUFWIDTH0: - case GE_CMD_TEXBUFWIDTH1: - case GE_CMD_TEXBUFWIDTH2: - case GE_CMD_TEXBUFWIDTH3: - case GE_CMD_TEXBUFWIDTH4: - case GE_CMD_TEXBUFWIDTH5: - case GE_CMD_TEXBUFWIDTH6: - case GE_CMD_TEXBUFWIDTH7: - return QString("Texture BUFWIDTHess %1: %2 width : %3").arg(cmd-GE_CMD_TEXBUFWIDTH0).arg(data,6,16,QChar('0')).arg(data & 0xFFFF); - - case GE_CMD_CLUTADDR: - return QString("CLUT base addr: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_CLUTADDRUPPER: - return QString("CLUT addr upper %1").arg(data,8,16,QChar('0')); - - case GE_CMD_LOADCLUT: - // This could be used to "dirty" textures with clut. - return QString("Clut load, numColor : %1").arg(data*8); - - case GE_CMD_TEXMAPMODE: - { - const char* texMapMode[3] = - { - "Texture Coordinates (UV)", - "Texture Matrix", - "Environment Map" - }; - const char* texProjMode[4] = - { - "Position", - "Texture Coordinates", - "Normalized Normal", - "Normal" - }; - return QString("Tex map mode: Map mode : %1, Proj Mode : %2").arg(texMapMode[data & 0x3]).arg(texProjMode[(data >> 8) & 0x3]); - } - case GE_CMD_TEXSHADELS: - return QString("Tex shade light sources: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_CLUTFORMAT: - { - const char* fmt[4] = - { - "16-bit BGR 5650", - "16-bit ABGR 5551", - "16-bit ABGR 4444", - "32-bit ABGR 8888" - }; - return QString("Clut format: %1 , Fmt : %2").arg(data,6,16,QChar('0')).arg(fmt[data & 0x3]); - } - - case GE_CMD_TRANSFERSRC: - { - return QString("Block Transfer Src: %1").arg(data,6,16,QChar('0')); - // Nothing to do, the next one prints - } - - case GE_CMD_TRANSFERSRCW: - { - u32 xferSrc = state.transfersrc | ((data&0xFF0000)<<8); - u32 xferSrcW = state.transfersrcw & 1023; - return QString("Block Transfer Src: %1 W: %2").arg(xferSrc,8,16,QChar('0')).arg(xferSrcW); - } - - case GE_CMD_TRANSFERDST: - { - // Nothing to do, the next one prints - return QString("Block Transfer Dst: %1").arg(data,6,16,QChar('0')); - } - - case GE_CMD_TRANSFERDSTW: - { - u32 xferDst= state.transferdst | ((data&0xFF0000)<<8); - u32 xferDstW = state.transferdstw & 1023; - return QString("Block Transfer Dest: %1 W: %2").arg(xferDst,8,16,QChar('0')).arg(xferDstW); - } - - case GE_CMD_TRANSFERSRCPOS: - { - u32 x = (data & 1023)+1; - u32 y = ((data>>10) & 1023)+1; - return QString("Block Transfer Src Rect TL: %1, %2").arg(x).arg(y); - } - - case GE_CMD_TRANSFERDSTPOS: - { - u32 x = (data & 1023)+1; - u32 y = ((data>>10) & 1023)+1; - return QString("Block Transfer Dest Rect TL: %1, %2").arg(x).arg(y); - } - - case GE_CMD_TRANSFERSIZE: - { - u32 w = (data & 1023)+1; - u32 h = ((data>>10) & 1023)+1; - return QString("Block Transfer Rect Size: %1 x %2").arg(w).arg(h); - } - - case GE_CMD_TRANSFERSTART: - { - return QString("Block Transfer Start : %1").arg(data ? "32-bit texel size" : "16-bit texel size"); - } - - case GE_CMD_TEXSIZE0: - case GE_CMD_TEXSIZE1: - case GE_CMD_TEXSIZE2: - case GE_CMD_TEXSIZE3: - case GE_CMD_TEXSIZE4: - case GE_CMD_TEXSIZE5: - case GE_CMD_TEXSIZE6: - case GE_CMD_TEXSIZE7: - { - int w = 1 << (data & 0xf); - int h = 1 << ((data>>8) & 0xf); - return QString("Texture Size %1: %2, width : %3, height : %4").arg(cmd - GE_CMD_TEXSIZE0).arg(data,6,16,QChar('0')).arg(w).arg(h); - } - - case GE_CMD_ZBUFPTR: - { - return QString("Zbuf Ptr: %1").arg(data,6,16,QChar('0')); - } - - case GE_CMD_ZBUFWIDTH: - { - return QString("Zbuf Width: %1").arg(data,6,16,QChar('0')); - } - - case GE_CMD_AMBIENTCOLOR: - return QString("Ambient Color: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_AMBIENTALPHA: - return QString("Ambient Alpha: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MATERIALAMBIENT: - return QString("Material Ambient Color: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MATERIALDIFFUSE: - return QString("Material Diffuse Color: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MATERIALEMISSIVE: - return QString("Material Emissive Color: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MATERIALSPECULAR: - return QString("Material Specular Color: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MATERIALALPHA: - return QString("Material Alpha Color: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MATERIALSPECULARCOEF: - return QString("Material specular coef: %1").arg(getFloat24(data)); - - case GE_CMD_SHADEMODE: - return QString("Shade: %1 (%2)").arg(data,6,16,QChar('0')).arg(data ? "gouraud" : "flat"); - - case GE_CMD_LIGHTMODE: - return QString("Lightmode: %1 (%2)").arg(data,6,16,QChar('0')).arg(data ? "separate spec" : "single color"); - - case GE_CMD_LIGHTTYPE0: - case GE_CMD_LIGHTTYPE1: - case GE_CMD_LIGHTTYPE2: - case GE_CMD_LIGHTTYPE3: - { - const char* lightType[3] = - { - "Directional Light", - "Point Light", - "Spot Light" - }; - const char* lightComp[3] = - { - "Ambient & Diffuse", - "Diffuse & Specular", - "Unknown (diffuse color, affected by specular power)" - }; - return QString("Light %1 type: %2 %3").arg(cmd-GE_CMD_LIGHTTYPE0).arg(lightType[(data) >> 8 & 0x3]).arg(lightComp[data & 0x3]); - } - case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0: - case GE_CMD_LX1:case GE_CMD_LY1:case GE_CMD_LZ1: - case GE_CMD_LX2:case GE_CMD_LY2:case GE_CMD_LZ2: - case GE_CMD_LX3:case GE_CMD_LY3:case GE_CMD_LZ3: - { - int n = cmd - GE_CMD_LX0; - int l = n / 3; - int c = n % 3; - float val = getFloat24(data); - return QString("Light %1 %2 pos: %3").arg(l).arg(QChar('X')+c).arg(val); - } - - case GE_CMD_LDX0:case GE_CMD_LDY0:case GE_CMD_LDZ0: - case GE_CMD_LDX1:case GE_CMD_LDY1:case GE_CMD_LDZ1: - case GE_CMD_LDX2:case GE_CMD_LDY2:case GE_CMD_LDZ2: - case GE_CMD_LDX3:case GE_CMD_LDY3:case GE_CMD_LDZ3: - { - int n = cmd - GE_CMD_LDX0; - int l = n / 3; - int c = n % 3; - float val = getFloat24(data); - return QString("Light %1 %2 dir: %3").arg(l).arg(QChar('X')+c).arg(val); - } - - case GE_CMD_LKA0:case GE_CMD_LKB0:case GE_CMD_LKC0: - case GE_CMD_LKA1:case GE_CMD_LKB1:case GE_CMD_LKC1: - case GE_CMD_LKA2:case GE_CMD_LKB2:case GE_CMD_LKC2: - case GE_CMD_LKA3:case GE_CMD_LKB3:case GE_CMD_LKC3: - { - int n = cmd - GE_CMD_LKA0; - int l = n / 3; - int c = n % 3; - float val = getFloat24(data); - return QString("Light %1 %2 att: %3").arg(l).arg(QChar('X')+c).arg(val); - } - - case GE_CMD_LAC0:case GE_CMD_LAC1:case GE_CMD_LAC2:case GE_CMD_LAC3: - case GE_CMD_LDC0:case GE_CMD_LDC1:case GE_CMD_LDC2:case GE_CMD_LDC3: - case GE_CMD_LSC0:case GE_CMD_LSC1:case GE_CMD_LSC2:case GE_CMD_LSC3: - { - float r = (float)(data & 0xff)/255.0f; - float g = (float)((data>>8) & 0xff)/255.0f; - float b = (float)(data>>16)/255.0f; - - int l = (cmd - GE_CMD_LAC0) / 3; - int t = (cmd - GE_CMD_LAC0) % 3; - return QString("Light %1 color %2: %3 %4 %5").arg(l).arg(t).arg(r).arg(g).arg(b); - } - - case GE_CMD_VIEWPORTXSCALE: - case GE_CMD_VIEWPORTYSCALE: - case GE_CMD_VIEWPORTXCENTER: - case GE_CMD_VIEWPORTYCENTER: - return QString("Viewport param %1: %2").arg(cmd-GE_CMD_VIEWPORTXSCALE).arg(getFloat24(data)); - case GE_CMD_VIEWPORTZSCALE: - { - float zScale = getFloat24(data) / 65535.f; - return QString("Viewport Z scale: %1").arg(zScale); - } - case GE_CMD_VIEWPORTZCENTER: - { - float zOff = getFloat24(data) / 65535.f; - return QString("Viewport Z pos: %1").arg(zOff); - } - - case GE_CMD_LIGHTENABLE0: - case GE_CMD_LIGHTENABLE1: - case GE_CMD_LIGHTENABLE2: - case GE_CMD_LIGHTENABLE3: - return QString("Light %1 enable: %2").arg(cmd-GE_CMD_LIGHTENABLE0).arg(data); - - case GE_CMD_CULL: - { - const char* cull[2] = - { - "Clockwise visible", - "Counter-clockwise visible" - }; - return QString("cull: %1").arg(cull[data & 0x1]); - } - case GE_CMD_PATCHDIVISION: - { - int patch_div_s = data & 0xFF; - int patch_div_t = (data >> 8) & 0xFF; - return QString("Patch subdivision: S=%1 x T=%2").arg(patch_div_s).arg(patch_div_t); - } - - case GE_CMD_PATCHPRIMITIVE: - { - const char* type[3] = - { - "Triangles", - "Lines", - "Points" - }; - return QString("Patch Primitive: %1").arg(type[data]); - } - case GE_CMD_PATCHFACING: - { - const char* val[2] = - { - "Clockwise", - "Counter-Clockwise" - }; - return QString( "Patch Facing: %1").arg(val[data]); - } - case GE_CMD_REVERSENORMAL: - return QString("Reverse normal: %1").arg(data); - break; - - case GE_CMD_MATERIALUPDATE: - { - QString txt = ""; - if(data & 1) txt += " Ambient"; - if(data & 2) txt += " Diffuse"; - if(data & 4) txt += " Specular"; - return QString("Material Update: %1").arg(txt); - } - - ////////////////////////////////////////////////////////////////// - // CLEARING - ////////////////////////////////////////////////////////////////// - case GE_CMD_CLEARMODE: - { - // If it becomes a performance problem, check diff&1 - const char* clearMode[8] = - { - "", - "Clear Color Buffer", - "Clear Stencil/Alpha Buffer", - "", - "Clear Depth Buffer", - "", - "", - "" - }; - return QString("Clear mode: %1, enabled : %2").arg(clearMode[(data >> 8) & 0xF]).arg(data & 0x1); - } - - ////////////////////////////////////////////////////////////////// - // ALPHA BLENDING - ////////////////////////////////////////////////////////////////// - case GE_CMD_ALPHABLENDENABLE: - return QString("Alpha blend enable: %1").arg(data); - - case GE_CMD_BLENDMODE: - { - const char* func[9] = - { - "Source Color", - "One Minus Source Color", - "Source Alpha", - "One Minus Source Alpha", - "Destination Color", - "One Minus Destination Color", - "Destination Alpha", - "One Minus Destination Alpha", - "Fix" - }; - const char* op[6] = - { - "Add", - "Subtract", - "Reverse Subtract", - "Minimum Value", - "Maximum Value", - "Absolute Value" - }; - return QString("Blend mode: Src : %1, Dest : %2, Op : %3").arg(func[(data >> 4) & 0xF]).arg(func[(data >> 8) & 0xF]).arg(op[(data) & 0x7]); - } - case GE_CMD_BLENDFIXEDA: - return QString("Blend fix A: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_BLENDFIXEDB: - return QString("Blend fix B: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_ALPHATESTENABLE: - return QString("Alpha test enable: %1").arg(data); - - case GE_CMD_ALPHATEST: - { - const char* testFunc[8] = - { - "Never pass pixel", - "Always pass pixel", - "Pass pixel if match", - "Pass pixel if difference", - "Pass pixel if less", - "Pass pixel if less or equal", - "Pass pixel if greater", - "Pass pixel if greater or equal" - }; - return QString("Alpha test settings, Mask : %1, Ref : %2, Test : %3").arg((data >> 8) & 0xFF).arg((data >> 16) & 0xFF).arg(testFunc[data & 0x7]); - } - case GE_CMD_ANTIALIASENABLE: - return QString("Antialias enable: %1").arg(data); - - case GE_CMD_PATCHCULLENABLE: - return QString("Antialias enable: %1").arg(data); - - case GE_CMD_COLORTESTENABLE: - { - const char* colorTest[4] = - { - "Never pass pixel", - "Always pass pixel", - "Pass pixel if color matches", - "Pass pixel if color differs" - }; - return QString("Color Test enable: %1").arg(colorTest[data]); - } - case GE_CMD_LOGICOPENABLE: - { - const char* logicOp[16] = - { - "Clear", - "And", - "Reverse And", - "Copy", - "Inverted And", - "No Operation", - "Exclusive Or", - "Or", - "Negated Or", - "Equivalence", - "Inverted", - "Reverse Or", - "Inverted Copy", - "Inverted Or", - "Negated And", - "Set" - }; - return QString("Logic op enable: %1").arg(logicOp[data]); - } - case GE_CMD_TEXFUNC: - { - const char* effect[8] = - { - "Modulate", - "Decal", - "Blend", - "Replace", - "Add", - "","","" - }; - return QString("TexFunc %1 / %2 / %3").arg((data&0x100)?"Texture alpha is read":"Texture alpha is ignored").arg((data & 0x10000)?"Fragment color is doubled":"Fragment color is untouched").arg(effect[data & 0x7]); - } - case GE_CMD_TEXFILTER: - { - const char* filter[8]= - { - "Nearest", - "Linear", - "", - "", - "Nearest; Mipmap Nearest", - "Linear; Mipmap Nearest", - "Nearest; Mipmap Linear", - "Linear; Mipmap Linear" - }; - int min = data & 0x7; - int mag = (data >> 8) & 0x7; - return QString("TexFilter min: %1 mag: %2").arg(filter[min]).arg(filter[mag]); - } - - case GE_CMD_TEXENVCOLOR: - return QString("TexEnvColor %1").arg(data,6,16,QChar('0')); - - case GE_CMD_TEXMODE: - { - u32 maxMipMap = (data >> 16) & 0xF; - return QString("TexMode MaxmipMap : %1, Swizzle : %2").arg(maxMipMap).arg(data & 0x1); - } - case GE_CMD_TEXFORMAT: - { - const char* texFmt[11] = - { - "16-bit BGR 5650", - "16-bit ABGR 5551", - "16-bit ABGR 4444", - "32-bit ABGR 8888", - "4-bit indexed", - "8-bit indexed", - "16-bit indexed", - "32-bit indexed", - "DXT1", - "DXT3", - "DXT5" - }; - return QString("TexFormat %1").arg(texFmt[data]); - } - case GE_CMD_TEXFLUSH: - return QString("TexFlush"); - - case GE_CMD_TEXSYNC: - return QString("TexSync"); - - case GE_CMD_TEXWRAP: - { - const char* wrapMode[2] = - { - "Repeat", - "Clamp" - }; - return QString("TexWrap U : %1, V : %2").arg(wrapMode[data & 0x1]).arg(wrapMode[(data >> 8) & 0x1]); - } - case GE_CMD_TEXLEVEL: - return QString("TexWrap Mode: %1 Offset: %2").arg(data&3).arg(data >> 16); - - case GE_CMD_FOG1: - return QString("Fog1 %1").arg(getFloat24(data)); - - case GE_CMD_FOG2: - return QString( "Fog2 %1").arg(getFloat24(data)); - - case GE_CMD_FOGCOLOR: - return QString("FogColor %1").arg(data,6,16,QChar('0')); - - case GE_CMD_TEXLODSLOPE: - return QString( "TexLodSlope %1").arg(data,6,16,QChar('0')); - - ////////////////////////////////////////////////////////////////// - // Z/STENCIL TESTING - ////////////////////////////////////////////////////////////////// - - case GE_CMD_ZTESTENABLE: - return QString( "Z test enable: %1").arg(data&1); - - case GE_CMD_STENCILOP: - { - const char* stencilOp[8] = - { - "Keep stencil value", - "Zero stencil value", - "Replace stencil value", - "Invert stencil value", - "Increment stencil value", - "Decrement stencil value", - "","" - }; - return QString("Stencil op: ZFail : %1, Fail : %2, Pass : %3").arg(stencilOp[(data >> 16) & 0x7]).arg(stencilOp[(data >> 8) & 0x7]).arg(stencilOp[(data) & 0x7]); - } - case GE_CMD_STENCILTEST: - { - const char* testFunc[8] = - { - "Never pass stencil pixel", - "Always pass stencil pixel", - "Pass test if match", - "Pass test if difference", - "Pass test if less", - "Pass test if less or equal", - "Pass test if greater", - "Pass test if greater or equal" - }; - return QString("Stencil test, Mask : %1, Ref : %2, Test : %3").arg((data >> 8) & 0xFF).arg((data >> 16) & 0xFF).arg(testFunc[data & 0x7]); - } - - case GE_CMD_STENCILTESTENABLE: - return QString("Stencil test enable: %1").arg(data); - - case GE_CMD_ZTEST: - { - const char* testFunc[8] = - { - "Never pass stencil pixel", - "Always pass stencil pixel", - "Pass pixel if match", - "Pass pixel if difference", - "Pass pixel if less", - "Pass pixel if less or equal", - "Pass pixel if greater", - "Pass pixel if greater or equal" - }; - return QString("Z test mode: %1").arg(testFunc[data & 0x7]); - } - case GE_CMD_MORPHWEIGHT0: - case GE_CMD_MORPHWEIGHT1: - case GE_CMD_MORPHWEIGHT2: - case GE_CMD_MORPHWEIGHT3: - case GE_CMD_MORPHWEIGHT4: - case GE_CMD_MORPHWEIGHT5: - case GE_CMD_MORPHWEIGHT6: - case GE_CMD_MORPHWEIGHT7: - { - int index = cmd - GE_CMD_MORPHWEIGHT0; - float weight = getFloat24(data); - return QString("MorphWeight %1 = %2").arg(index).arg(weight); - } - - case GE_CMD_DITH0: - case GE_CMD_DITH1: - case GE_CMD_DITH2: - case GE_CMD_DITH3: - return QString("DitherMatrix %1 = %2").arg(cmd-GE_CMD_DITH0).arg(data,6,16,QChar('0')); - - case GE_CMD_LOGICOP: - return QString("LogicOp: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_ZWRITEDISABLE: - return QString("ZMask: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_COLORTEST: - return QString("ColorTest: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_COLORREF: - return QString("ColorRef: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_COLORTESTMASK: - return QString( "ColorTestMask: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MASKRGB: - return QString("MaskRGB: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_MASKALPHA: - return QString("MaskAlpha: %1").arg(data,6,16,QChar('0')); - - case GE_CMD_WORLDMATRIXNUMBER: - return QString("World # %1").arg(data & 0xF); - - case GE_CMD_WORLDMATRIXDATA: - return QString("World data # %1").arg(getFloat24(data)); - - case GE_CMD_VIEWMATRIXNUMBER: - return QString("VIEW # %1").arg(data & 0xF); - - case GE_CMD_VIEWMATRIXDATA: - return QString("VIEW data # %1").arg(getFloat24(data)); - - case GE_CMD_PROJMATRIXNUMBER: - return QString("PROJECTION # %1").arg(data & 0xF); - - case GE_CMD_PROJMATRIXDATA: - return QString("PROJECTION matrix data # %1").arg(getFloat24(data)); - - case GE_CMD_TGENMATRIXNUMBER: - return QString("TGEN # %1").arg(data & 0xF); - - case GE_CMD_TGENMATRIXDATA: - return QString("TGEN data # %1").arg(getFloat24(data)); - - case GE_CMD_BONEMATRIXNUMBER: - return QString("BONE #%1").arg(data); - - case GE_CMD_BONEMATRIXDATA: - return QString("BONE data #%1 %2").arg(state.boneMatrixNumber & 0x7f).arg(getFloat24(data)); - - default: - return QString("Unknown: %1").arg(op,8,16,QChar('0')); - } -} - - -void Debugger_DisplayList::FillDisplayListCmd(std::map& data, u32 pc, u32 prevAddr, GPUgstate& state) -{ - u32 curPc = pc; - int debugLimit = 10000; // Anti crash if this code is bugged - while(Memory::IsValidAddress(curPc) && debugLimit > 0) - { - if(data.find(curPc) != data.end()) - return; - - u32 op = Memory::ReadUnchecked_U32(curPc); //read from memory - u32 cmd = op >> 24; - u32 data_ = op & 0xFFFFFF; - u32 diff = op ^ gstate.cmdmem[cmd]; - state.cmdmem[cmd] = op; - u32 prevOp = 0; - if(Memory::IsValidAddress(prevAddr)) - Memory::ReadUnchecked_U32(prevAddr); - data[curPc].comment = DisassembleOp(curPc, op, prevOp, state); - data[curPc].addr = curPc; - data[curPc].cmd = cmd; - data[curPc].data = data_; - data[curPc].implementationNotFinished = false; - data[curPc].texAddr = (gstate.texaddr[0] & 0xFFFFF0) | ((gstate.texbufwidth[0]<<8) & 0x0F000000); - data[curPc].fboAddr = state.fbptr & 0xFFFFFF; - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (state.vaddr & 0xFFFFFF); - data[curPc].vtxAddr = ((state.offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - baseExtended = ((state.base & 0x0F0000) << 8) | (state.iaddr & 0xFFFFFF); - data[curPc].idxAddr = ((state.offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - // Add or remove bugged functions for highlight - if(cmd == GE_CMD_BEZIER || - cmd == GE_CMD_SPLINE || - cmd == GE_CMD_BJUMP || - cmd == GE_CMD_BOUNDINGBOX) - { - data[curPc].implementationNotFinished = true; - } - - // We are drawing, save the GPU state for texture, vertex and index list - if(cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE) - { - drawGPUState.push_back(state); - } - - if(cmd == GE_CMD_JUMP) - { - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (data_ & 0xFFFFFF); - u32 target = (((state.offsetAddr & 0xFFFFFF) << 8) + baseExtended) & 0x0FFFFFFF; - FillDisplayListCmd(data, target, prevAddr, state); - return; - } - else if(cmd == GE_CMD_CALL) - { - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (data_ & 0xFFFFFF); - u32 target = (((state.offsetAddr & 0xFFFFFF) << 8) + baseExtended) & 0x0FFFFFFF; - FillDisplayListCmd(data, target, prevAddr, state); - } - else if(cmd == GE_CMD_RET) - { - return; - } - else if(cmd == GE_CMD_FINISH) - { - return; - } - else if(cmd == GE_CMD_END) - { - if(prevOp >> 24 == GE_CMD_FINISH) - return; - } - prevAddr = curPc; - curPc += 4; - debugLimit--; - } -} - -void Debugger_DisplayList::Update() -{ - if(!isVisible()) - return; - UpdateRenderBuffer(); - UpdateRenderBufferList(); - UpdateDisplayList(); -} - - -void Debugger_DisplayList::on_displayList_itemClicked(QTreeWidgetItem *item, int column) -{ - displayListRowSelected = item; - ShowDLCode(); -} - -void Debugger_DisplayList::on_stepBtn_clicked() -{ - ((QtHost*)host)->SetGPUStep(true); -} - -void Debugger_DisplayList::on_runBtn_clicked() -{ - ui->displayList->clear(); - ui->displayListData->clear(); - ((QtHost*)host)->SetGPUStep(false); -} - -void Debugger_DisplayList::on_stopBtn_clicked() -{ - ((QtHost*)host)->SetGPUStep(true); -} - -void Debugger_DisplayList::UpdateRenderBuffer() -{ - emit updateRenderBuffer_(); -} - -void Debugger_DisplayList::UpdateRenderBufferGUI() -{ - //gpu->Flush(); - - int FRAME_WIDTH; - int FRAME_HEIGHT; - u8 *data = 0; - int curTex; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &curTex); - // TODO: GLES2 compatible -#ifndef USING_GLES2 - if(currentTextureDisplay == 0) - { - FRAME_WIDTH = pixel_xres; - FRAME_HEIGHT = pixel_yres; - data = new u8[FRAME_WIDTH * FRAME_HEIGHT * 4]; - memset(data,0,FRAME_WIDTH * FRAME_HEIGHT * 4); - if(currentRenderFrameDisplay == 0) - { - glReadBuffer(GL_COLOR_ATTACHMENT0); - glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, data); - } - else - { - glReadBuffer(GL_DEPTH_ATTACHMENT); - glReadPixels(0, 0, FRAME_WIDTH, FRAME_HEIGHT, GL_DEPTH_COMPONENT, GL_FLOAT, data); - } - } - else - { - draw_->GetFramebufferDimensions(currentTextureDisplay, &FRAME_WIDTH, &FRAME_HEIGHT); - data = new u8[FRAME_WIDTH * FRAME_HEIGHT * 4]; - memset(data,0,FRAME_WIDTH * FRAME_HEIGHT * 4); - if(currentRenderFrameDisplay == 0) - { - draw_->BindFramebufferAsTexture(currentTextureDisplay, 0, Draw::FB_COLOR_BIT, 0); - glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); - } - } -#endif - glBindTexture(GL_TEXTURE_2D, curTex); - - QImage img = QImage(data, FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH*4, QImage::Format_ARGB32).mirrored(false,true); - QPixmap pixmap = QPixmap::fromImage(img); - ui->fboImg->setPixmap(pixmap); - ui->fboImg->setMinimumWidth(pixmap.width() * fboZoomFactor); - ui->fboImg->setMinimumHeight(pixmap.height() * fboZoomFactor); - ui->fboImg->setMaximumWidth(pixmap.width() * fboZoomFactor); - ui->fboImg->setMaximumHeight(pixmap.height() * fboZoomFactor); - - delete[] data; -} - -void Debugger_DisplayList::on_nextDrawBtn_clicked() -{ - ((QtHost*)host)->SetGPUStep(true, 1); -} - -void Debugger_DisplayList::on_gotoPCBtn_clicked() -{ - if(!displayListRowSelected) - return; - u32 currentPC = displayListRowSelected->data(3, Qt::UserRole).toInt(); - - for(int i = 0; i < ui->displayListData->topLevelItemCount(); i++) - { - if((u32)ui->displayListData->topLevelItem(i)->data(0, Qt::UserRole).toInt() == currentPC) - { - ui->displayListData->setCurrentItem(ui->displayListData->topLevelItem(i)); - } - } -} - -void Debugger_DisplayList::on_texturesList_itemDoubleClicked(QTreeWidgetItem *item, int column) -{ - mainWindow->GetDialogMemoryTex()->ShowTex(drawGPUState[item->data(0,Qt::UserRole).toInt()]); -} - -void Debugger_DisplayList::on_comboBox_currentIndexChanged(int index) -{ - currentRenderFrameDisplay = index; - UpdateRenderBufferGUI(); -} - -void Debugger_DisplayList::UpdateRenderBufferList() -{ - emit updateRenderBufferList_(); -} - -void Debugger_DisplayList::UpdateRenderBufferListGUI() -{ - ui->fboList->clear(); - - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,"Framebuffer"); - item->setData(0,Qt::UserRole, 0); - item->setText(1,QString::number(pixel_xres)); - item->setText(2,QString::number(pixel_yres)); - item->setText(3,QString::number(4)); - ui->fboList->addTopLevelItem(item); - - std::vector fboList = gpu->GetFramebufferList(); - - for(size_t i = 0; i < fboList.size(); i++) - { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0,QString("%1").arg(fboList[i].fb_address,8,16,QChar('0'))); - u64 addr = (u64)fboList[i].fbo; - item->setData(0,Qt::UserRole, addr); - item->setData(0,Qt::UserRole+1, fboList[i].fb_address); - item->setText(1,QString::number(fboList[i].width)); - item->setText(2,QString::number(fboList[i].height)); - item->setText(3,QString::number(fboList[i].format)); - - ui->fboList->addTopLevelItem(item); - } -} - -void Debugger_DisplayList::on_fboList_itemClicked(QTreeWidgetItem *item, int column) -{ - u64 addr = item->data(0,Qt::UserRole).toULongLong(); - Draw::Framebuffer * fbo = (Draw::Framebuffer *)addr; - currentTextureDisplay = fbo; - UpdateRenderBufferGUI(); -} - -void Debugger_DisplayList::on_nextDLBtn_clicked() -{ - ((QtHost*)host)->SetGPUStep(true,-1); -} - -void Debugger_DisplayList::setCurrentFBO(u32 addr) -{ - for(int i = 0; i < ui->fboList->topLevelItemCount(); i++) - { - if((u32)ui->fboList->topLevelItem(i)->data(0,Qt::UserRole+1).toInt() == addr) - { - for(int j = 0; j < ui->fboList->colorCount(); j++) - ui->fboList->topLevelItem(i)->setTextColor(j,Qt::green); - } - else - { - for(int j = 0; j < ui->fboList->colorCount(); j++) - ui->fboList->topLevelItem(i)->setTextColor(j,Qt::black); - } - } -} - -void Debugger_DisplayList::on_zoommBtn_clicked() -{ - fboZoomFactor *= 0.5; - ui->fboImg->setMinimumWidth(ui->fboImg->minimumWidth()*0.5); - ui->fboImg->setMinimumHeight(ui->fboImg->minimumHeight()*0.5); - ui->fboImg->setMaximumWidth(ui->fboImg->minimumWidth()*0.5); - ui->fboImg->setMaximumHeight(ui->fboImg->minimumHeight()*0.5); -} - -void Debugger_DisplayList::on_zoompBtn_clicked() -{ - fboZoomFactor *= 2; - ui->fboImg->setMinimumWidth(ui->fboImg->minimumWidth()*2); - ui->fboImg->setMinimumHeight(ui->fboImg->minimumHeight()*2); - ui->fboImg->setMaximumWidth(ui->fboImg->minimumWidth()*2); - ui->fboImg->setMaximumHeight(ui->fboImg->minimumHeight()*2); -} - -void Debugger_DisplayList::UpdateVertexInfo() -{ - //commented out until someone bothers to maintain it -#if 0 - ui->vertexData->clear(); - - QTreeWidgetItem* item = ui->vertexList->currentItem(); - if(item == 0) - return; - - GPUgstate state = drawGPUState[item->data(0,Qt::UserRole).toInt()]; - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (state.vaddr & 0xFFFFFF); - u32 vaddr = ((state.offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - - VertexDecoder vtcDec; - vtcDec.SetVertexType(state.vertType); - u8* tmp = new u8[20*vtcDec.GetDecVtxFmt().stride]; - vtcDec.DecodeVerts(tmp,Memory::GetPointer(vaddr),0,19); - VertexReader vtxRead(tmp,vtcDec.GetDecVtxFmt(),state.vertType); - - for(int i = 0; i < maxVtxDisplay; i++) - { - vtxRead.Goto(i); - QTreeWidgetItem* itemTop = new QTreeWidgetItem(); - itemTop->setText(0,QString::number(i)); - itemTop->setText(1,QString("%1").arg(vaddr+i*vtcDec.GetDecVtxFmt().stride,8,16,QChar('0'))); - ui->vertexData->addTopLevelItem(itemTop); - - if (vtxRead.hasNormal()) - { - float nrm[3]; - vtxRead.ReadNrm(nrm); - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(1,"Normal"); - item->setText(2,QString("X: %1, Y: %2, Z: %3").arg(nrm[0]).arg(nrm[1]).arg(nrm[2])); - itemTop->addChild(item); - } - if (vtxRead.hasUV()) { - float uv[2]; - vtxRead.ReadUV(uv); - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(1,"Uv"); - item->setText(2,QString("X: %1, Y: %2").arg(uv[0]).arg(uv[1])); - itemTop->addChild(item); - } - if (vtxRead.hasColor0()) { - float col0[4]; - vtxRead.ReadColor0(col0); - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(1,"Color0"); - item->setText(2,QString("X: %1, Y: %2, Z: %3").arg(col0[0]).arg(col0[1]).arg(col0[2])); - itemTop->addChild(item); - } - if (vtxRead.hasColor0()) { - float col1[3]; - vtxRead.ReadColor1(col1); - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(1,"Color1"); - item->setText(2,QString("X: %1, Y: %2, Z: %3").arg(col1[0]).arg(col1[1]).arg(col1[2])); - itemTop->addChild(item); - } - float pos[3]; - vtxRead.ReadPos(pos); - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(1,"Position"); - item->setText(2,QString("X: %1, Y: %2, Z: %3").arg(pos[0]).arg(pos[1]).arg(pos[2])); - itemTop->addChild(item); - } - delete [] tmp; - for(int i = 0; i < ui->vertexData->columnCount(); i++) - { - ui->vertexData->resizeColumnToContents(i); - } -#endif -} - -void Debugger_DisplayList::on_vertexList_itemClicked(QTreeWidgetItem *item, int column) -{ - UpdateVertexInfo(); -} - -void Debugger_DisplayList::on_pushButton_clicked() -{ - maxVtxDisplay += 20; - UpdateVertexInfo(); -} - - -void Debugger_DisplayList::UpdateIndexInfo() -{ - ui->indexData->clear(); - - QTreeWidgetItem* item = ui->indexList->currentItem(); - if(item == 0) - return; - - GPUgstate state = drawGPUState[item->data(0,Qt::UserRole).toInt()]; - u32 baseExtended = ((state.base & 0x0F0000) << 8) | (state.iaddr & 0xFFFFFF); - u32 iaddr = ((state.offsetAddr & 0xFFFFFF) + baseExtended) & 0x0FFFFFFF; - - int sizeIdx = 1; - if((state.vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_16BIT) - sizeIdx = 2; - - for(int i = 0; i < maxIdxDisplay; i++) - { - QTreeWidgetItem* itemTop = new QTreeWidgetItem(); - itemTop->setText(0,QString::number(i)); - itemTop->setText(1,QString("%1").arg(iaddr+i*sizeIdx,8,16,QChar('0'))); - int idx = 0; - if(sizeIdx == 1) - idx = Memory::Read_U8(iaddr+i*sizeIdx); - else - idx = Memory::Read_U16(iaddr+i*sizeIdx); - itemTop->setText(2,QString::number(idx)); - ui->indexData->addTopLevelItem(itemTop); - } - for(int i = 0; i < ui->indexData->columnCount(); i++) - { - ui->indexData->resizeColumnToContents(i); - } -} - - -void Debugger_DisplayList::on_nextIdx_clicked() -{ - maxIdxDisplay += 20; - UpdateIndexInfo(); -} - -void Debugger_DisplayList::on_indexList_itemClicked(QTreeWidgetItem *item, int column) -{ - UpdateIndexInfo(); -} - -void Debugger_DisplayList::on_displayListData_customContextMenuRequested(const QPoint &pos) -{ - QTreeWidgetItem* item = ui->displayListData->itemAt(pos); - if(!item) - return; - displayListDataSelected = item; - - QMenu menu(this); - - QAction *runToHere = new QAction(tr("Run to here"), this); - connect(runToHere, SIGNAL(triggered()), this, SLOT(RunToDLPC())); - menu.addAction(runToHere); - - menu.exec( ui->displayListData->mapToGlobal(pos)); -} - -void Debugger_DisplayList::RunToDLPC() -{ - u32 addr = displayListDataSelected->text(0).toUInt(0,16); - ((QtHost*)host)->SetGPUStep(true, 2, addr); -} - -void Debugger_DisplayList::on_texturesList_customContextMenuRequested(const QPoint &pos) -{ - QTreeWidgetItem* item = ui->texturesList->itemAt(pos); - if(!item) - return; - textureDataSelected = item; - - QMenu menu(this); - - QAction *runToDraw = new QAction(tr("Run to draw using this texture"), this); - connect(runToDraw, SIGNAL(triggered()), this, SLOT(RunToDrawTex())); - menu.addAction(runToDraw); - - menu.exec( ui->texturesList->mapToGlobal(pos)); -} - -void Debugger_DisplayList::RunToDrawTex() -{ - u32 addr = textureDataSelected->text(0).toUInt(0,16); - ((QtHost*)host)->SetGPUStep(true, 3, addr); -} diff --git a/Qt/Debugger/debugger_displaylist.h b/Qt/Debugger/debugger_displaylist.h deleted file mode 100644 index 2c88384269..0000000000 --- a/Qt/Debugger/debugger_displaylist.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include "Core/Debugger/DebugInterface.h" - -#include -#include - -#include "GPU/GPUState.h" -#include "thin3d/thin3d.h" - -class MainWindow; -namespace Ui { -class Debugger_DisplayList; -} - - -class DListLine -{ -public: - u32 addr; - u32 cmd; - u32 data; - QString comment; - bool implementationNotFinished; - u32 texAddr; - u32 fboAddr; - u32 vtxAddr; - int vtxStart; - int vtxCount; - u32 idxAddr; - int idxStart; - int idxCount; -}; - -class Debugger_DisplayList : public QDialog -{ - Q_OBJECT - -public: - Debugger_DisplayList(DebugInterface *_cpu, Draw::DrawContext *draw, MainWindow *mainWindow_, QWidget *parent = 0); - ~Debugger_DisplayList(); - - void UpdateDisplayList(); - - void ShowDLCode(); - void FillDisplayListCmd(std::map &data, u32 pc, u32 prev, GPUgstate &state); - void Update(); - void UpdateRenderBuffer(); - void UpdateRenderBufferList(); - void UpdateVertexInfo(); - void UpdateIndexInfo(); - -signals: - void updateDisplayList_(); - void updateRenderBufferList_(); - void updateRenderBuffer_(); - -private slots: - void UpdateDisplayListGUI(); - void UpdateRenderBufferListGUI(); - void UpdateRenderBufferGUI(); - - void on_displayList_itemClicked(QTreeWidgetItem *item, int column); - void on_stepBtn_clicked(); - void on_runBtn_clicked(); - void on_stopBtn_clicked(); - void on_nextDrawBtn_clicked(); - void on_gotoPCBtn_clicked(); - void on_texturesList_itemDoubleClicked(QTreeWidgetItem *item, int column); - void on_comboBox_currentIndexChanged(int index); - void on_fboList_itemClicked(QTreeWidgetItem *item, int column); - void on_nextDLBtn_clicked(); - void setCurrentFBO(u32 addr); - void on_zoommBtn_clicked(); - void on_zoompBtn_clicked(); - void on_vertexList_itemClicked(QTreeWidgetItem *item, int column); - void on_pushButton_clicked(); - void on_nextIdx_clicked(); - void on_indexList_itemClicked(QTreeWidgetItem *item, int column); - void on_displayListData_customContextMenuRequested(const QPoint &pos); - void on_texturesList_customContextMenuRequested(const QPoint &pos); - void RunToDLPC(); - void RunToDrawTex(); - -private: - QString DisassembleOp(u32 pc, u32 op, u32 prev, const GPUgstate &state); - - Ui::Debugger_DisplayList *ui; - DebugInterface* cpu; - Draw::DrawContext* draw_; - MainWindow* mainWindow; - QTreeWidgetItem* displayListRowSelected; - QTreeWidgetItem* displayListDataSelected; - QTreeWidgetItem* textureDataSelected; - int currentRenderFrameDisplay; - Draw::Framebuffer *currentTextureDisplay; - float fboZoomFactor; - int maxVtxDisplay; - int maxIdxDisplay; - - std::vector drawGPUState; - std::map vtxBufferSize; - std::map idxBufferSize; -}; diff --git a/Qt/Debugger/debugger_displaylist.ui b/Qt/Debugger/debugger_displaylist.ui deleted file mode 100644 index 894a479ad2..0000000000 --- a/Qt/Debugger/debugger_displaylist.ui +++ /dev/null @@ -1,533 +0,0 @@ - - - Debugger_DisplayList - - - - 0 - 0 - 795 - 506 - - - - Dialog - - - - - - Qt::Horizontal - - - - Qt::Vertical - - - - DisplayList - - - - - - Qt::CustomContextMenu - - - false - - - false - - - false - - - 30 - - - - Id - - - - - Status - - - - - Start Address - - - - - Current Address - - - - - - - - - - Run - - - - - - - Stop - - - - - - - Next DL - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 0 - - - - Commands - - - - - - Qt::CustomContextMenu - - - false - - - false - - - false - - - 2 - - - false - - - - 1 - - - - - 2 - - - - - - - - - - Step - - - - - - - Next Draw - - - - - - - Goto PC - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Textures - - - - - - Qt::CustomContextMenu - - - - Address - - - - - Width - - - - - Height - - - - - Format - - - - - - - - - Vertex Buffer - - - - - - 30 - - - - Address - - - - - Coord Type - - - - - Number Morph - - - - - Number Weights - - - - - Has Weight - - - - - Has Position - - - - - Has Normal - - - - - Has Color - - - - - Has UV - - - - - - - - 30 - - - - Idx - - - - - Address - - - - - Values - - - - - - - - - - Next 20 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Index Buffer - - - - - - Qt::Vertical - - - - - Address - - - - - - 30 - - - - Idx - - - - - Adress - - - - - Value - - - - - - - - - - - Next 20 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - Framebuffer - - - - - - Qt::Vertical - - - - - 0 - 0 - - - - - VAddress - - - - - Width - - - - - Height - - - - - Format - - - - - - - 0 - 0 - - - - true - - - - - 0 - 0 - 375 - 76 - - - - - - - QFrame::Box - - - - - - true - - - - - - - - - - - - - - Display : - - - - - - - - Color - - - - - Depth - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Zoom- - - - - - - - Zoom+ - - - - - - - - - - - - - - diff --git a/Qt/QtHost.h b/Qt/QtHost.h index 5a0f201b10..8557bef3e4 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -28,8 +28,6 @@ public: QtHost(MainWindow *mainWindow_) { mainWindow = mainWindow_; - m_GPUStep = false; - m_GPUFlag = 0; } virtual void UpdateUI() override { @@ -42,8 +40,6 @@ public: } virtual void UpdateDisassembly() override { mainWindow->updateMenus(); - if(mainWindow->GetDialogDisplaylist()) - mainWindow->GetDialogDisplaylist()->Update(); } virtual void SetDebugMode(bool mode) override { @@ -97,24 +93,9 @@ public: } bool GPUDebuggingActive() override { - auto dialogDisplayList = mainWindow->GetDialogDisplaylist(); - if (dialogDisplayList && dialogDisplayList->isVisible()) { - if (m_GPUStep && m_GPUFlag == -1) - m_GPUFlag = 0; - - return true; - } return false; } - void SetGPUStep(bool value, int flag = 0, u32 data = 0) { - m_GPUStep = value; - m_GPUFlag = flag; - m_GPUData = data; - } private: const char* SymbolMapFilename(std::string currentFilename); MainWindow* mainWindow; - bool m_GPUStep; - int m_GPUFlag; - u32 m_GPUData; }; diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index e5c1ee978f..104d7a95f6 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -22,8 +22,7 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : nextState(CORE_POWERDOWN), lastUIState(UISTATE_MENU), memoryWindow(0), - memoryTexWindow(0), - displaylistWindow(0) + memoryTexWindow(0) { QDesktopWidget *desktop = QApplication::desktop(); int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt(); @@ -149,7 +148,6 @@ void MainWindow::bootDone() memoryWindow = new Debugger_Memory(currentDebugMIPS, this, this); memoryTexWindow = new Debugger_MemoryTex(this); - displaylistWindow = new Debugger_DisplayList(currentDebugMIPS, gpu->GetDrawContext(), this, this); notifyMapsLoaded(); @@ -178,8 +176,6 @@ void MainWindow::closeAct() memoryWindow->close(); if(memoryTexWindow && memoryTexWindow->isVisible()) memoryTexWindow->close(); - if(displaylistWindow && displaylistWindow->isVisible()) - displaylistWindow->close(); NativeMessageReceived("stop", ""); SetGameTitle(""); @@ -264,8 +260,6 @@ void MainWindow::resetAct() memoryWindow->close(); if(memoryTexWindow) memoryTexWindow->close(); - if(displaylistWindow) - displaylistWindow->close(); NativeMessageReceived("reset", ""); } @@ -322,12 +316,6 @@ void MainWindow::dumpNextAct() gpu->DumpNextFrame(); } -void MainWindow::dpyListAct() -{ - if(displaylistWindow) - displaylistWindow->show(); -} - void MainWindow::consoleAct() { LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden()); @@ -520,9 +508,6 @@ void MainWindow::createMenus() debugMenu->add(new MenuAction(this, SLOT(takeScreen()), QT_TR_NOOP("Take Screenshot"), Qt::Key_F12)) ->addDisableState(UISTATE_MENU); debugMenu->addSeparator(); - //commented out until someone bothers to maintain it - //debugMenu->add(new MenuAction(this, SLOT(dpyListAct()), QT_TR_NOOP("Display List..."))) - // ->addDisableState(UISTATE_MENU); debugMenu->add(new MenuAction(this, SLOT(consoleAct()), QT_TR_NOOP("Log Console"))) ->addDisableState(UISTATE_MENU); debugMenu->add(new MenuAction(this, SLOT(memviewAct()), QT_TR_NOOP("Memory View"))) diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index 4f702bfc87..699bb4f8d8 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -14,7 +14,6 @@ #include "Core/System.h" #include "Debugger/debugger_memory.h" #include "Debugger/debugger_memorytex.h" -#include "Debugger/debugger_displaylist.h" #include "Qt/QtMain.h" extern bool g_TakeScreenshot; @@ -38,7 +37,6 @@ public: Debugger_Memory* GetDialogMemory() { return memoryWindow; } Debugger_MemoryTex* GetDialogMemoryTex() { return memoryTexWindow; } - Debugger_DisplayList* GetDialogDisplaylist() { return displaylistWindow; } CoreState GetNextState() { return nextState; } void ShowMemory(u32 addr); @@ -95,7 +93,6 @@ private slots: void resetTableAct(); void dumpNextAct(); void takeScreen() { g_TakeScreenshot = true; } - void dpyListAct(); void consoleAct(); void memviewAct(); void memviewTexAct(); @@ -166,7 +163,6 @@ private: Debugger_Memory *memoryWindow; Debugger_MemoryTex *memoryTexWindow; - Debugger_DisplayList *displaylistWindow; QActionGroup *anisotropicGroup, *screenGroup, *displayLayoutGroup, *defaultLogGroup, *g3dLogGroup, *hleLogGroup; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 1020d0b76b..da523a6f71 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -347,12 +347,6 @@ true true - - true - true - true - true - true true @@ -465,7 +459,6 @@ - @@ -527,7 +520,6 @@ - diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 9e263f79d9..553198c60a 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -161,9 +161,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger @@ -326,9 +323,6 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger @@ -441,9 +435,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger From cf33e852ef790a68ee9443eb5838a8a2489afa51 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 11:20:59 -0700 Subject: [PATCH 115/301] Qt Debugger: Remove memory viewer. Being replaced by WebSocket based debugger. --- CMakeLists.txt | 5 - Qt/Debugger/ctrlmemview.cpp | 259 -------------------------------- Qt/Debugger/ctrlmemview.h | 91 ----------- Qt/Debugger/debugger_memory.cpp | 89 ----------- Qt/Debugger/debugger_memory.h | 45 ------ Qt/Debugger/debugger_memory.ui | 92 ------------ Qt/QtHost.h | 2 - Qt/mainwindow.cpp | 30 ---- Qt/mainwindow.h | 6 - Windows/PPSSPP.vcxproj | 15 -- Windows/PPSSPP.vcxproj.filters | 15 -- 11 files changed, 649 deletions(-) delete mode 100644 Qt/Debugger/ctrlmemview.cpp delete mode 100644 Qt/Debugger/ctrlmemview.h delete mode 100644 Qt/Debugger/debugger_memory.cpp delete mode 100644 Qt/Debugger/debugger_memory.h delete mode 100644 Qt/Debugger/debugger_memory.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 187fc2e21c..351d0e9381 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -757,7 +757,6 @@ elseif(USING_QT_UI) find_package(Qt5 COMPONENTS Multimedia) endif(NOT SDL2_FOUND) set(Qt_UI - Qt/Debugger/debugger_memory.ui Qt/Debugger/debugger_memorytex.ui ) qt5_wrap_ui(QT_UI_GEN ${Qt_UI}) @@ -769,10 +768,6 @@ elseif(USING_QT_UI) Qt/QtHost.h Qt/mainwindow.cpp Qt/mainwindow.h - Qt/Debugger/ctrlmemview.cpp - Qt/Debugger/ctrlmemview.h - Qt/Debugger/debugger_memory.cpp - Qt/Debugger/debugger_memory.h Qt/Debugger/debugger_memorytex.cpp Qt/Debugger/debugger_memorytex.h ) diff --git a/Qt/Debugger/ctrlmemview.cpp b/Qt/Debugger/ctrlmemview.cpp deleted file mode 100644 index 65079a0a04..0000000000 --- a/Qt/Debugger/ctrlmemview.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "ctrlmemview.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "math.h" - -#include "Core/MemMap.h" -#include "Core/Debugger/SymbolMap.h" - -CtrlMemView::CtrlMemView(QWidget *parent) : - QWidget(parent) -{ - curAddress=0; - rowHeight=14; - align=4; - alignMul=4; - selecting=false; - mode=MV_NORMAL; - debugger = 0; - - setMinimumWidth(500); -} - -void CtrlMemView::redraw() -{ - update(); -} - -void CtrlMemView::wheelEvent(QWheelEvent* e) -{ - int numDegrees = e->delta() / 8; - int numSteps = numDegrees / 15; - if (e->orientation() == Qt::Vertical) - { - curAddress -= numSteps*align*alignMul; - redraw(); - } -} - -void CtrlMemView::keyPressEvent(QKeyEvent *e) -{ - int page=(rect().bottom()/rowHeight)/2-1; - - switch (e->key()) - { - case Qt::Key_Up: curAddress -= align*alignMul; break; - case Qt::Key_Down: curAddress += align*alignMul; break; - case Qt::Key_PageUp: curAddress -= page*align*alignMul; break; - case Qt::Key_PageDown: curAddress += page*align*alignMul; break; - default: QWidget::keyPressEvent(e); break; - } - - redraw(); -} - -void CtrlMemView::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - painter.setBrush(Qt::white); - painter.setPen(Qt::white); - painter.drawRect(rect()); - - if (!debugger) - return; - - int width = rect().width(); - int numRows=(rect().bottom()/rowHeight)/2+1; - - QPen nullPen(0xFFFFFF); - QPen currentPen(0xFF000000); - QPen selPen(0x808080); - QBrush lbr(0xFFFFFF); - QBrush nullBrush(0xFFFFFF); - QBrush currentBrush(0xFFEFE8); - QBrush pcBrush(0x70FF70); - QPen textPen; - - QFont normalFont("Arial", 10); - QFont alignedFont("Monospace", 10); - alignedFont.setStyleHint(QFont::Monospace); - painter.setFont(normalFont); - - int i; - curAddress&=~(align-1); - for (i=-numRows; i<=numRows; i++) - { - unsigned int address=curAddress + i*align*alignMul; - - int rowY1 = rect().bottom()/2 + rowHeight*i - rowHeight/2; - int rowY2 = rect().bottom()/2 + rowHeight*i + rowHeight/2; - - char temp[256]; - - painter.setBrush(currentBrush); - - if (selecting && address == (unsigned int)selection) - painter.setPen(selPen); - else - painter.setPen(i==0 ? currentPen : nullPen); - painter.drawRect(0, rowY1, 16-1, rowY2 - rowY1 - 1); - - painter.drawRect(16, rowY1, width - 16 -1, rowY2 - rowY1 - 1); - painter.setBrush(nullBrush); - textPen.setColor(0x600000); - painter.setPen(textPen); - painter.setFont(alignedFont); - painter.drawText(17,rowY1-2+rowHeight, QString("%1").arg(address,8,16,QChar('0'))); - textPen.setColor(0xFF000000); - painter.setPen(textPen); - if (debugger->isAlive()) - { - - switch(mode) { - case MV_NORMAL: - { - const char *m = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - if (Memory::IsValidAddress(address)) - { - u32 memory[4] = { - debugger->readMemory(address), - debugger->readMemory(address+4), - debugger->readMemory(address+8), - debugger->readMemory(address+12) - }; - m = (const char*)memory; - sprintf(temp, "%08x %08x %08x %08x ................", - memory[0],memory[1],memory[2],memory[3]); - } - for (int i=0; i<16; i++) - { - int c = (unsigned char)m[i]; - if (c>=32 && c<255) - temp[i+37]=c; - } - } - painter.setFont(alignedFont); - painter.drawText(85,rowY1 - 2 + rowHeight, temp); - break; - - case MV_SYMBOLS: - { -/* textPen.setColor(0x0000FF); - painter.setPen(textPen); - int fn = g_symbolMap->GetSymbolNum(address); - if (fn==-1) - { - sprintf(temp, "%s (ns)", Memory::GetAddressName(address)); - } - else - sprintf(temp, "%s (0x%x b)", g_symbolMap->GetSymbolName(fn),g_symbolMap->GetSymbolSize(fn)); - painter.drawText(205,rowY1 - 2 + rowHeight, temp); - - textPen.setColor(0xFF000000); - painter.setPen(textPen); - - if (align==4) - { - u32 value = Memory::ReadUnchecked_U32(address); - int symbolnum = g_symbolMap->GetSymbolNum(value); - if(symbolnum>=0) - sprintf(temp, "%08x [%s]", value, g_symbolMap->GetSymbolName(symbolnum)); - } - else if (align==2) - { - u16 value = Memory::ReadUnchecked_U16(address); - int symbolnum = g_symbolMap->GetSymbolNum(value); - if(symbolnum>=0) - sprintf(temp, "%04x [%s]", value, g_symbolMap->GetSymbolName(symbolnum)); - } - - painter.drawText(85,rowY1 - 2 + rowHeight, temp);*/ - break; - } - case MV_MAX: break; - } - } - } -} - -void CtrlMemView::mousePressEvent(QMouseEvent *e) -{ - int x = e->pos().x(); - int y = e->pos().y(); - if (x>16) - { - oldSelection=selection; - selection=yToAddress(y); - bool oldselecting=selecting; - selecting=true; - if (!oldselecting || (selection!=oldSelection)) - redraw(); - } -} - -void CtrlMemView::contextMenu(const QPoint &pos) -{ - QMenu menu(this); - - QAction *gotoDisAsm = new QAction(tr("Go to in &disasm"), this); - //connect(gotoDisAsm, SIGNAL(triggered()), this, SLOT(GotoDisAsm())); - menu.addAction(gotoDisAsm); - - menu.addSeparator(); - - QAction *copyValue = new QAction(tr("&Copy value"), this); - connect(copyValue, SIGNAL(triggered()), this, SLOT(CopyValue())); - menu.addAction(copyValue); - - QAction *changeValue = new QAction(tr("C&hange value"), this); - connect(changeValue, SIGNAL(triggered()), this, SLOT(Change())); - menu.addAction(changeValue); - - QAction *dump = new QAction(tr("Dump..."), this); - connect(dump, SIGNAL(triggered()), this, SLOT(Dump())); - menu.addAction(dump); - - menu.exec( mapToGlobal(pos)); -} - -void CtrlMemView::CopyValue() -{ - QApplication::clipboard()->setText(QString("%1").arg(Memory::ReadUnchecked_U32(selection),8,16,QChar('0'))); -} - -void CtrlMemView::Dump() -{ - QMessageBox::information(this,"Sorry","This feature has not been implemented.",QMessageBox::Ok); -} - - -void CtrlMemView::Change() -{ - QString curVal = QString("%1").arg(Memory::ReadUnchecked_U32(selection),8,16,QChar('0')); - - bool ok; - QString text = QInputDialog::getText(this, tr("Set new value"), - tr("Set new value:"), QLineEdit::Normal, - curVal, &ok); - if (ok && !text.isEmpty()) - { - Memory::WriteUnchecked_U32(text.toUInt(0,16),selection); - redraw(); - } -} - - -int CtrlMemView::yToAddress(int y) -{ - int ydiff=y-rect().bottom()/2-rowHeight/2; - ydiff=(int)(floor((float)ydiff / (float)rowHeight))+1; - return curAddress + ydiff * align*alignMul; -} - diff --git a/Qt/Debugger/ctrlmemview.h b/Qt/Debugger/ctrlmemview.h deleted file mode 100644 index e4b52cfe63..0000000000 --- a/Qt/Debugger/ctrlmemview.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef CTRLMEMVIEW_H -#define CTRLMEMVIEW_H - -#include "Core/Debugger/DebugInterface.h" -#include - -enum MemViewMode -{ - MV_NORMAL, - MV_SYMBOLS, - MV_MAX -}; - -class CtrlMemView : public QWidget -{ - Q_OBJECT -public: - explicit CtrlMemView(QWidget *parent = 0); - - void setDebugger(DebugInterface *deb) - { - debugger=deb; - if (debugger) - align=debugger->getInstructionSize(0); - } - DebugInterface *getDebugger() - { - return debugger; - } - void redraw(); - - void setMode(MemViewMode m) - { - mode=m; - switch(mode) { - case MV_NORMAL: - alignMul=4; - break; - case MV_SYMBOLS: - alignMul=1; - break; - default: - break; - } - redraw(); - } - - void setAlign(int l) - { - align=l; - } - int yToAddress(int y); - - void gotoAddr(unsigned int addr) - { - curAddress=addr&(~(align-1)); - redraw(); - } - - unsigned int getSelection() - { - return curAddress; - } - void contextMenu(const QPoint &pos); -protected: - void paintEvent(QPaintEvent *); - void keyPressEvent(QKeyEvent *e); - void wheelEvent(QWheelEvent *e); - void mousePressEvent(QMouseEvent *e); - -public slots: - void CopyValue(); - void Dump(); - void Change(); -private: - int curAddress; - int align; - int alignMul; - int rowHeight; - - int selection; - int oldSelection; - bool selectionChanged; - bool selecting; - bool hasFocus; - - DebugInterface *debugger; - MemViewMode mode; -}; - -#endif // CTRLMEMVIEW_H diff --git a/Qt/Debugger/debugger_memory.cpp b/Qt/Debugger/debugger_memory.cpp deleted file mode 100644 index 706fd8dcfd..0000000000 --- a/Qt/Debugger/debugger_memory.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "debugger_memory.h" -#include "ui_debugger_memory.h" -#include "Core/Debugger/SymbolMap.h" -#include - -Debugger_Memory::Debugger_Memory(DebugInterface *_cpu, MainWindow* mainWindow_, QWidget *parent) : - QDialog(parent), - ui(new Ui::Debugger_Memory), - cpu(_cpu), - mainWindow(mainWindow_) -{ - ui->setupUi(this); - - setWindowTitle(tr("Memory Viewer - %1").arg(cpu->GetName())); - - ui->memView->setDebugger(_cpu); -} - -Debugger_Memory::~Debugger_Memory() -{ - delete ui; -} - -void Debugger_Memory::Update() -{ - ui->memView->redraw(); -} - -void Debugger_Memory::Goto(u32 addr) -{ - show(); - ui->memView->gotoAddr(addr & ~3); -} - -void Debugger_Memory::on_editAddress_textChanged(const QString &arg1) -{ - ui->memView->gotoAddr(arg1.toUInt(0,16) & ~3); -} - -void Debugger_Memory::on_normalBtn_clicked() -{ - ui->memView->setMode(MV_NORMAL); -} - -void Debugger_Memory::on_symbolsBtn_clicked() -{ - ui->memView->setMode(MV_SYMBOLS); -} - -void Debugger_Memory::on_memView_customContextMenuRequested(const QPoint &pos) -{ - ui->memView->contextMenu(pos); -} - -void Debugger_Memory::NotifyMapLoaded() -{ - QListWidgetItem* item = new QListWidgetItem(); - item->setText("(0x80000000)"); - item->setData(Qt::UserRole, 0x80000000); - ui->symbols->addItem(item); - - std::vector symbols = g_symbolMap->GetAllSymbols(ST_DATA); - for(int i = 0; i < (int)symbols.size(); i++) - { - QListWidgetItem* item = new QListWidgetItem(); - item->setText(QString("%1 (%2)").arg(QString::fromStdString(symbols[i].name)).arg(symbols[i].size)); - item->setData(Qt::UserRole, symbols[i].address); - ui->symbols->addItem(item); - } - - ui->regions->clear(); - /* - for (int i = 0; i < cpu->getMemMap()->numRegions; i++) - { - int n = ComboBox_AddString(lb,cpu->getMemMap()->regions[i].name); - ComboBox_SetItemData(lb,n,cpu->getMemMap()->regions[i].start); - } - */ -} - -void Debugger_Memory::on_regions_currentIndexChanged(int index) -{ - ui->memView->gotoAddr(ui->regions->itemData(index,Qt::UserRole).toInt()); -} - -void Debugger_Memory::on_symbols_itemClicked(QListWidgetItem *item) -{ - ui->memView->gotoAddr(item->data(Qt::UserRole).toInt()); -} diff --git a/Qt/Debugger/debugger_memory.h b/Qt/Debugger/debugger_memory.h deleted file mode 100644 index c4593a5fef..0000000000 --- a/Qt/Debugger/debugger_memory.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef DEBUGGER_MEMORY_H -#define DEBUGGER_MEMORY_H - -#include "Core/Debugger/DebugInterface.h" -#include -#include - -class MainWindow; -namespace Ui { -class Debugger_Memory; -} - -class Debugger_Memory : public QDialog -{ - Q_OBJECT - -public: - explicit Debugger_Memory(DebugInterface *_cpu, MainWindow* mainWindow_, QWidget *parent = 0); - ~Debugger_Memory(); - - void Update(); - void Goto(u32 addr); - - void NotifyMapLoaded(); -private slots: - void on_editAddress_textChanged(const QString &arg1); - - void on_normalBtn_clicked(); - - void on_symbolsBtn_clicked(); - - void on_memView_customContextMenuRequested(const QPoint &pos); - - void on_regions_currentIndexChanged(int index); - - void on_symbols_itemClicked(QListWidgetItem *item); - -private: - Ui::Debugger_Memory *ui; - DebugInterface* cpu; - MainWindow* mainWindow; - -}; - -#endif // DEBUGGER_MEMORY_H diff --git a/Qt/Debugger/debugger_memory.ui b/Qt/Debugger/debugger_memory.ui deleted file mode 100644 index abb94dcf32..0000000000 --- a/Qt/Debugger/debugger_memory.ui +++ /dev/null @@ -1,92 +0,0 @@ - - - Debugger_Memory - - - - 0 - 0 - 611 - 384 - - - - Dialog - - - - - - - - Goto: - - - - - - - - - - - - - Mode - - - - - - Normal - - - - - - - Symbols - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - Qt::CustomContextMenu - - - - - - - - - - CtrlMemView - QWidget -
ctrlmemview.h
- 1 -
-
- - -
diff --git a/Qt/QtHost.h b/Qt/QtHost.h index 8557bef3e4..fbad0aa7ea 100644 --- a/Qt/QtHost.h +++ b/Qt/QtHost.h @@ -35,8 +35,6 @@ public: } virtual void UpdateMemView() override { - if(mainWindow->GetDialogMemory()) - mainWindow->GetDialogMemory()->Update(); } virtual void UpdateDisassembly() override { mainWindow->updateMenus(); diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 104d7a95f6..4fa8d9a6d2 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -21,7 +21,6 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : currentLanguage("en"), nextState(CORE_POWERDOWN), lastUIState(UISTATE_MENU), - memoryWindow(0), memoryTexWindow(0) { QDesktopWidget *desktop = QApplication::desktop(); @@ -47,12 +46,6 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : QObject::connect(emugl, SIGNAL(newFrame()), this, SLOT(newFrame())); } -void MainWindow::ShowMemory(u32 addr) -{ - if(memoryWindow) - memoryWindow->Goto(addr); -} - inline float clamp1(float x) { if (x > 1.0f) return 1.0f; if (x < -1.0f) return -1.0f; @@ -146,11 +139,8 @@ void MainWindow::bootDone() if(g_Config.bFullScreen != isFullScreen()) fullscrAct(); - memoryWindow = new Debugger_Memory(currentDebugMIPS, this, this); memoryTexWindow = new Debugger_MemoryTex(this); - notifyMapsLoaded(); - if (nextState == CORE_RUNNING) runAct(); updateMenus(); @@ -172,8 +162,6 @@ void MainWindow::closeAct() { updateMenus(); - if(memoryWindow && memoryWindow->isVisible()) - memoryWindow->close(); if(memoryTexWindow && memoryTexWindow->isVisible()) memoryTexWindow->close(); @@ -256,8 +244,6 @@ void MainWindow::resetAct() { updateMenus(); - if(memoryWindow) - memoryWindow->close(); if(memoryTexWindow) memoryTexWindow->close(); @@ -285,7 +271,6 @@ void MainWindow::lmapAct() { QString fileName = QFileInfo(fileNames[0]).absoluteFilePath(); g_symbolMap->LoadSymbolMap(fileName.toStdString().c_str()); - notifyMapsLoaded(); } } @@ -308,7 +293,6 @@ void MainWindow::smapAct() void MainWindow::resetTableAct() { g_symbolMap->Clear(); - notifyMapsLoaded(); } void MainWindow::dumpNextAct() @@ -321,12 +305,6 @@ void MainWindow::consoleAct() LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden()); } -void MainWindow::memviewAct() -{ - if (memoryWindow) - memoryWindow->show(); -} - void MainWindow::memviewTexAct() { if(memoryTexWindow) @@ -510,8 +488,6 @@ void MainWindow::createMenus() debugMenu->addSeparator(); debugMenu->add(new MenuAction(this, SLOT(consoleAct()), QT_TR_NOOP("Log Console"))) ->addDisableState(UISTATE_MENU); - debugMenu->add(new MenuAction(this, SLOT(memviewAct()), QT_TR_NOOP("Memory View"))) - ->addDisableState(UISTATE_MENU); debugMenu->add(new MenuAction(this, SLOT(memviewTexAct()),QT_TR_NOOP("Memory View Texture"))) ->addDisableState(UISTATE_MENU); @@ -634,9 +610,3 @@ void MainWindow::createMenus() retranslate(); } - -void MainWindow::notifyMapsLoaded() -{ - if (memoryWindow) - memoryWindow->NotifyMapLoaded(); -} diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index 699bb4f8d8..4b99975d52 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -12,7 +12,6 @@ #include "Core/Core.h" #include "Core/Config.h" #include "Core/System.h" -#include "Debugger/debugger_memory.h" #include "Debugger/debugger_memorytex.h" #include "Qt/QtMain.h" @@ -35,11 +34,9 @@ public: explicit MainWindow(QWidget *parent = 0, bool fullscreen=false); ~MainWindow() { }; - Debugger_Memory* GetDialogMemory() { return memoryWindow; } Debugger_MemoryTex* GetDialogMemoryTex() { return memoryTexWindow; } CoreState GetNextState() { return nextState; } - void ShowMemory(u32 addr); void updateMenus(); void Notify(MainWindowMsg msg) { @@ -94,7 +91,6 @@ private slots: void dumpNextAct(); void takeScreen() { g_TakeScreenshot = true; } void consoleAct(); - void memviewAct(); void memviewTexAct(); // Options @@ -153,7 +149,6 @@ private: void SetGameTitle(QString text); void loadLanguage(const QString &language, bool retranslate); void createMenus(); - void notifyMapsLoaded(); QTranslator translator; QString currentLanguage; @@ -161,7 +156,6 @@ private: CoreState nextState; GlobalUIState lastUIState; - Debugger_Memory *memoryWindow; Debugger_MemoryTex *memoryTexWindow; QActionGroup *anisotropicGroup, *screenGroup, *displayLayoutGroup, diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index da523a6f71..85113285de 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -341,18 +341,6 @@
- - true - true - true - true - - - true - true - true - true - true true @@ -458,8 +446,6 @@ - - @@ -520,7 +506,6 @@ - diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 553198c60a..bf27441486 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -161,15 +161,9 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Windows\System @@ -323,15 +317,9 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Windows\System @@ -435,9 +423,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Other Platforms\Qt\Debugger From ec398cbb1fa0fb041736dda5f4ed8db1da4b679d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 3 Jun 2018 11:27:26 -0700 Subject: [PATCH 116/301] Qt Debugger: Remove texture viewer. No longer worked anyway. Being replaced by WebSocket debugger. --- CMakeLists.txt | 7 -- Qt/Debugger/debugger_memorytex.cpp | 60 ---------- Qt/Debugger/debugger_memorytex.h | 27 ----- Qt/Debugger/debugger_memorytex.ui | 177 ----------------------------- Qt/mainwindow.cpp | 20 +--- Qt/mainwindow.h | 5 - Windows/PPSSPP.vcxproj | 8 -- Windows/PPSSPP.vcxproj.filters | 9 -- 8 files changed, 1 insertion(+), 312 deletions(-) delete mode 100644 Qt/Debugger/debugger_memorytex.cpp delete mode 100644 Qt/Debugger/debugger_memorytex.h delete mode 100644 Qt/Debugger/debugger_memorytex.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 351d0e9381..f56b075c2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -756,20 +756,13 @@ elseif(USING_QT_UI) if(NOT SDL2_FOUND) find_package(Qt5 COMPONENTS Multimedia) endif(NOT SDL2_FOUND) - set(Qt_UI - Qt/Debugger/debugger_memorytex.ui - ) - qt5_wrap_ui(QT_UI_GEN ${Qt_UI}) list(APPEND NativeAppSource - ${QT_UI_GEN} Qt/QtMain.cpp Qt/QtMain.h Qt/QtHost.cpp Qt/QtHost.h Qt/mainwindow.cpp Qt/mainwindow.h - Qt/Debugger/debugger_memorytex.cpp - Qt/Debugger/debugger_memorytex.h ) add_definitions(-DUSING_QT_UI) include_directories(${CMAKE_CURRENT_BINARY_DIR} Qt Qt/Debugger) diff --git a/Qt/Debugger/debugger_memorytex.cpp b/Qt/Debugger/debugger_memorytex.cpp deleted file mode 100644 index 238c1371af..0000000000 --- a/Qt/Debugger/debugger_memorytex.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "debugger_memorytex.h" -#include "gfx/gl_common.h" -#include "ui_debugger_memorytex.h" -#include "Core/MemMap.h" -#include -#include -#include "Core/HLE/sceDisplay.h" -#include "GPU/GPUInterface.h" -#include "base/display.h" - -Debugger_MemoryTex::Debugger_MemoryTex(QWidget *parent) : - QDialog(parent), - ui(new Ui::Debugger_MemoryTex) -{ - ui->setupUi(this); -} - -Debugger_MemoryTex::~Debugger_MemoryTex() -{ - delete ui; -} - -void Debugger_MemoryTex::ShowTex(const GPUgstate &state) -{ - ui->texaddr->setText(QString("%1").arg(state.texaddr[0] & 0xFFFFFF,8,16,QChar('0'))); - ui->texbufwidth0->setText(QString("%1").arg(state.texbufwidth[0] & 0xFFFFFF,8,16,QChar('0'))); - ui->texformat->setText(QString("%1").arg(state.texformat & 0xFFFFFF,8,16,QChar('0'))); - ui->texsize->setText(QString("%1").arg(state.texsize[0] & 0xFFFFFF,8,16,QChar('0'))); - ui->texmode->setText(QString("%1").arg(state.texmode & 0xFFFFFF,8,16,QChar('0'))); - ui->clutformat->setText(QString("%1").arg(state.clutformat & 0xFFFFFF,8,16,QChar('0'))); - ui->clutaddr->setText(QString("%1").arg(state.clutaddr & 0xFFFFFF,8,16,QChar('0'))); - ui->clutaddrupper->setText(QString("%1").arg(state.clutaddrupper & 0xFFFFFF,8,16,QChar('0'))); - ui->loadclut->setText(QString("%1").arg(state.loadclut & 0xFFFFFF,8,16,QChar('0'))); - on_readBtn_clicked(); - - show(); -} - -void Debugger_MemoryTex::on_readBtn_clicked() -{ - GPUgstate state; - state.texaddr[0] = ui->texaddr->text().toUInt(0,16); - state.texbufwidth[0] = ui->texbufwidth0->text().toUInt(0,16); - state.texformat = ui->texformat->text().toUInt(0,16); - state.texsize[0] = ui->texsize->text().toUInt(0,16); - state.texmode = ui->texmode->text().toUInt(0,16); - state.clutformat = ui->clutformat->text().toUInt(0,16); - state.clutaddr = ui->clutaddr->text().toUInt(0,16); - state.clutaddrupper = ui->clutaddrupper->text().toUInt(0,16); - state.loadclut = ui->loadclut->text().toUInt(0,16); - int bufW = state.texbufwidth[0] & 0x3ff; - int w = 1 << (state.texsize[0] & 0xf); - int h = 1 << ((state.texsize[0]>>8) & 0xf); - w = std::max(bufW,w); - uchar* newData = new uchar[w*h*4]; - - // TODO: Use GPU_GetCurrentTexture(). Warning: blocks. - - delete[] newData; -} diff --git a/Qt/Debugger/debugger_memorytex.h b/Qt/Debugger/debugger_memorytex.h deleted file mode 100644 index a39d3ed53b..0000000000 --- a/Qt/Debugger/debugger_memorytex.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef DEBUGGER_MEMORYTEX_H -#define DEBUGGER_MEMORYTEX_H - -#include -#include "GPU/GPUState.h" - -namespace Ui { -class Debugger_MemoryTex; -} - -class Debugger_MemoryTex : public QDialog -{ - Q_OBJECT - -public: - explicit Debugger_MemoryTex(QWidget *parent = 0); - ~Debugger_MemoryTex(); - - void ShowTex(const GPUgstate& state); -private slots: - void on_readBtn_clicked(); - -private: - Ui::Debugger_MemoryTex *ui; -}; - -#endif // DEBUGGER_MEMORYTEX_H diff --git a/Qt/Debugger/debugger_memorytex.ui b/Qt/Debugger/debugger_memorytex.ui deleted file mode 100644 index b163010222..0000000000 --- a/Qt/Debugger/debugger_memorytex.ui +++ /dev/null @@ -1,177 +0,0 @@ - - - Debugger_MemoryTex - - - - 0 - 0 - 768 - 546 - - - - Dialog - - - - - - Qt::Horizontal - - - - - - - - - TexAddr - - - - - - - TexBufWidth0 - - - - - - - TexFormat - - - - - - - TexSize - - - - - - - ClutFormat - - - - - - - ClutAddr - - - - - - - ClutAddrUpper - - - - - - - LoadClut - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TexMode - - - - - - - - - - - - - - Read - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - true - - - - - 0 - 0 - 76 - 526 - - - - - - - QFrame::Box - - - - - - - - - - - - - - - - diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 4fa8d9a6d2..904edc6095 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -20,8 +20,7 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : QMainWindow(parent), currentLanguage("en"), nextState(CORE_POWERDOWN), - lastUIState(UISTATE_MENU), - memoryTexWindow(0) + lastUIState(UISTATE_MENU) { QDesktopWidget *desktop = QApplication::desktop(); int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt(); @@ -139,8 +138,6 @@ void MainWindow::bootDone() if(g_Config.bFullScreen != isFullScreen()) fullscrAct(); - memoryTexWindow = new Debugger_MemoryTex(this); - if (nextState == CORE_RUNNING) runAct(); updateMenus(); @@ -162,9 +159,6 @@ void MainWindow::closeAct() { updateMenus(); - if(memoryTexWindow && memoryTexWindow->isVisible()) - memoryTexWindow->close(); - NativeMessageReceived("stop", ""); SetGameTitle(""); } @@ -244,9 +238,6 @@ void MainWindow::resetAct() { updateMenus(); - if(memoryTexWindow) - memoryTexWindow->close(); - NativeMessageReceived("reset", ""); } @@ -305,15 +296,8 @@ void MainWindow::consoleAct() LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden()); } -void MainWindow::memviewTexAct() -{ - if(memoryTexWindow) - memoryTexWindow->show(); -} - void MainWindow::raiseTopMost() { - setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); raise(); activateWindow(); @@ -488,8 +472,6 @@ void MainWindow::createMenus() debugMenu->addSeparator(); debugMenu->add(new MenuAction(this, SLOT(consoleAct()), QT_TR_NOOP("Log Console"))) ->addDisableState(UISTATE_MENU); - debugMenu->add(new MenuAction(this, SLOT(memviewTexAct()),QT_TR_NOOP("Memory View Texture"))) - ->addDisableState(UISTATE_MENU); // Options MenuTree* optionsMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&Options")); diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index 4b99975d52..57d24acce1 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -12,7 +12,6 @@ #include "Core/Core.h" #include "Core/Config.h" #include "Core/System.h" -#include "Debugger/debugger_memorytex.h" #include "Qt/QtMain.h" extern bool g_TakeScreenshot; @@ -34,7 +33,6 @@ public: explicit MainWindow(QWidget *parent = 0, bool fullscreen=false); ~MainWindow() { }; - Debugger_MemoryTex* GetDialogMemoryTex() { return memoryTexWindow; } CoreState GetNextState() { return nextState; } void updateMenus(); @@ -91,7 +89,6 @@ private slots: void dumpNextAct(); void takeScreen() { g_TakeScreenshot = true; } void consoleAct(); - void memviewTexAct(); // Options // Core @@ -156,8 +153,6 @@ private: CoreState nextState; GlobalUIState lastUIState; - Debugger_MemoryTex *memoryTexWindow; - QActionGroup *anisotropicGroup, *screenGroup, *displayLayoutGroup, *defaultLogGroup, *g3dLogGroup, *hleLogGroup; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 85113285de..7fa9fc4525 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -341,12 +341,6 @@ - - true - true - true - true - true true @@ -446,7 +440,6 @@ - @@ -506,7 +499,6 @@ - diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index bf27441486..8cfb5cdbb3 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -161,9 +161,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Windows\System @@ -317,9 +314,6 @@ Other Platforms\Qt\Debugger - - Other Platforms\Qt\Debugger - Windows\System @@ -423,9 +417,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - From e9cc1ee46395599dd90d263a399b5d15bed12805 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 19 Apr 2018 21:04:47 -0700 Subject: [PATCH 117/301] Debugger: Allow multiple clients more properly. Otherwise we lose wakeups and hang. --- Core/Core.cpp | 10 +++++----- GPU/Debugger/Stepping.cpp | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index d549d79c4f..9d4a280e2c 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -98,7 +98,7 @@ void Core_Halt(const char *msg) { void Core_Stop() { Core_UpdateState(CORE_POWERDOWN); Core_NotifyShutdown(); - m_StepCond.notify_one(); + m_StepCond.notify_all(); } bool Core_IsStepping() { @@ -239,11 +239,11 @@ void Core_RunLoop(GraphicsContext *ctx) { void Core_DoSingleStep() { singleStepPending = true; - m_StepCond.notify_one(); + m_StepCond.notify_all(); } void Core_UpdateSingleStep() { - m_StepCond.notify_one(); + m_StepCond.notify_all(); } void Core_SingleStep() { @@ -253,7 +253,7 @@ void Core_SingleStep() { static inline void CoreStateProcessed() { if (coreStatePending) { coreStatePending = false; - m_InactiveCond.notify_one(); + m_InactiveCond.notify_all(); } } @@ -338,6 +338,6 @@ void Core_EnableStepping(bool step) { host->SetDebugMode(false); coreState = CORE_RUNNING; coreStatePending = false; - m_StepCond.notify_one(); + m_StepCond.notify_all(); } } diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index 7e4962753a..c765d15b76 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -67,7 +67,7 @@ static void SetPauseAction(PauseAction act, bool waitComplete = true) { pauseLock.unlock(); actionComplete = false; - pauseWait.notify_one(); + pauseWait.notify_all(); while (waitComplete && !actionComplete) { actionWait.wait(guard); } @@ -113,7 +113,7 @@ static void RunPauseAction() { } actionComplete = true; - actionWait.notify_one(); + actionWait.notify_all(); pauseAction = PAUSE_BREAK; } @@ -200,7 +200,7 @@ void ResumeFromStepping() { void ForceUnpause() { SetPauseAction(PAUSE_CONTINUE, false); actionComplete = true; - actionWait.notify_one(); + actionWait.notify_all(); } } // namespace From 77609bbea280d2d079bf7bbca22546826c5e1f54 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 19 Apr 2018 21:14:01 -0700 Subject: [PATCH 118/301] Debugger: Track a counter when entering stepping. This allows us to poll coreState and know if a new stepping needs to be sent. This is useful to allow e.g. regs to show changes per step. --- Core/Core.cpp | 6 ++++++ Core/Core.h | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 9d4a280e2c..0c5b37ddc1 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -54,6 +54,7 @@ static std::mutex m_hStepMutex; static std::condition_variable m_InactiveCond; static std::mutex m_hInactiveMutex; static bool singleStepPending = false; +static int steppingCounter = 0; static std::set shutdownFuncs; static bool windowHidden = false; static double lastActivity = 0.0; @@ -334,6 +335,7 @@ void Core_EnableStepping(bool step) { sleep_ms(1); host->SetDebugMode(true); Core_UpdateState(CORE_STEPPING); + steppingCounter++; } else { host->SetDebugMode(false); coreState = CORE_RUNNING; @@ -341,3 +343,7 @@ void Core_EnableStepping(bool step) { m_StepCond.notify_all(); } } + +int Core_GetSteppingCounter() { + return steppingCounter; +} diff --git a/Core/Core.h b/Core/Core.h index 9adbd6356e..1c98ccdc49 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -31,12 +31,12 @@ void Core_ErrorPause(); // For platforms that don't call Core_Run void Core_SetGraphicsContext(GraphicsContext *ctx); -void Core_RunRenderThreadFrame(); - // called from gui void Core_EnableStepping(bool step); void Core_DoSingleStep(); void Core_UpdateSingleStep(); +// Changes every time we enter stepping. +int Core_GetSteppingCounter(); typedef void (* Core_ShutdownFunc)(); void Core_ListenShutdown(Core_ShutdownFunc func); From e313a9bf6ce4f08a4dc68645d784df6bfafff780 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Apr 2018 08:33:22 -0700 Subject: [PATCH 119/301] Debugger: Lock startup/shutdown for threadsafety. Otherwise things can get freed while we're trying to inspect them. --- Core/Core.cpp | 9 ++++----- Core/Core.h | 15 ++++++++++++--- Core/HLE/sceIo.cpp | 10 ++++++---- Core/System.cpp | 19 ++++++++++++++----- GPU/Debugger/Stepping.cpp | 10 ++++++---- GPU/Debugger/Stepping.h | 3 ++- Windows/GEDebugger/GEDebugger.cpp | 2 +- 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 0c5b37ddc1..722fed54d2 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -55,7 +55,7 @@ static std::condition_variable m_InactiveCond; static std::mutex m_hInactiveMutex; static bool singleStepPending = false; static int steppingCounter = 0; -static std::set shutdownFuncs; +static std::set shutdownFuncs; static bool windowHidden = false; static double lastActivity = 0.0; static double lastKeepAwake = 0.0; @@ -76,13 +76,13 @@ void Core_NotifyActivity() { lastActivity = time_now_d(); } -void Core_ListenShutdown(Core_ShutdownFunc func) { +void Core_ListenLifecycle(CoreLifecycleFunc func) { shutdownFuncs.insert(func); } -void Core_NotifyShutdown() { +void Core_NotifyLifecycle(CoreLifecycle stage) { for (auto it = shutdownFuncs.begin(); it != shutdownFuncs.end(); ++it) { - (*it)(); + (*it)(stage); } } @@ -98,7 +98,6 @@ void Core_Halt(const char *msg) { void Core_Stop() { Core_UpdateState(CORE_POWERDOWN); - Core_NotifyShutdown(); m_StepCond.notify_all(); } diff --git a/Core/Core.h b/Core/Core.h index 1c98ccdc49..fe881f2aa5 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -38,9 +38,18 @@ void Core_UpdateSingleStep(); // Changes every time we enter stepping. int Core_GetSteppingCounter(); -typedef void (* Core_ShutdownFunc)(); -void Core_ListenShutdown(Core_ShutdownFunc func); -void Core_NotifyShutdown(); +enum class CoreLifecycle { + STARTING, + // Note: includes failure cases. Guaranteed call after STARTING. + START_COMPLETE, + STOPPING, + // Guaranteed call after STOPPING. + STOPPED, +}; + +typedef void (* CoreLifecycleFunc)(CoreLifecycle stage); +void Core_ListenLifecycle(CoreLifecycleFunc func); +void Core_NotifyLifecycle(CoreLifecycle stage); void Core_Halt(const char *msg); bool Core_IsStepping(); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 77e1a24113..2436c69bd4 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -496,10 +496,12 @@ static void __IoManagerThread() { } } -static void __IoWakeManager() { +static void __IoWakeManager(CoreLifecycle stage) { // Ping the thread so that it knows to check coreState. - ioManagerThreadEnabled = false; - ioManager.FinishEventLoop(); + if (stage == CoreLifecycle::STOPPING) { + ioManagerThreadEnabled = false; + ioManager.FinishEventLoop(); + } } static void __IoVblank() { @@ -586,7 +588,7 @@ void __IoInit() { ioManagerThreadEnabled = g_Config.bSeparateIOThread; ioManager.SetThreadEnabled(ioManagerThreadEnabled); if (ioManagerThreadEnabled) { - Core_ListenShutdown(&__IoWakeManager); + Core_ListenLifecycle(&__IoWakeManager); ioManagerThread = new std::thread(&__IoManagerThread); ioManagerThread->detach(); } diff --git a/Core/System.cpp b/Core/System.cpp index 92bf4c8701..afe1ac76ea 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -317,6 +317,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { INFO_LOG(BOOT, "PPSSPP %s", PPSSPP_GIT_VERSION); #endif + Core_NotifyLifecycle(CoreLifecycle::STARTING); GraphicsContext *temp = coreParameter.graphicsContext; coreParameter = coreParam; if (coreParameter.graphicsContext == nullptr) { @@ -331,6 +332,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { *error_string = coreParameter.errorString; bool success = coreParameter.fileToStart != ""; if (!success) { + Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); pspIsIniting = false; } return success; @@ -361,6 +363,9 @@ bool PSP_InitUpdate(std::string *error_string) { pspIsInited = GPU_IsReady(); pspIsIniting = !pspIsInited; + if (pspIsInited) { + Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); + } return pspIsInited; } @@ -386,17 +391,20 @@ void PSP_Shutdown() { return; } + // Make sure things know right away that PSP memory, etc. is going away. + pspIsQuitting = true; + if (coreState == CORE_RUNNING) + Core_UpdateState(CORE_ERROR); + #ifndef MOBILE_DEVICE if (g_Config.bFuncHashMap) { MIPSAnalyst::StoreHashMap(); } #endif - // Make sure things know right away that PSP memory, etc. is going away. - pspIsQuitting = true; - if (coreState == CORE_RUNNING) - Core_UpdateState(CORE_ERROR); - Core_NotifyShutdown(); + if (pspIsIniting) + Core_NotifyLifecycle(CoreLifecycle::START_COMPLETE); + Core_NotifyLifecycle(CoreLifecycle::STOPPING); CPU_Shutdown(); GPU_Shutdown(); g_paramSFO.Clear(); @@ -406,6 +414,7 @@ void PSP_Shutdown() { pspIsIniting = false; pspIsQuitting = false; g_Config.unloadGameConfig(); + Core_NotifyLifecycle(CoreLifecycle::STOPPED); } void PSP_BeginHostFrame() { diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index c765d15b76..ea93778cff 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -197,10 +197,12 @@ void ResumeFromStepping() { SetPauseAction(PAUSE_CONTINUE, false); } -void ForceUnpause() { - SetPauseAction(PAUSE_CONTINUE, false); - actionComplete = true; - actionWait.notify_all(); +void ForceUnpause(CoreLifecycle stage) { + if (stage == CoreLifecycle::STOPPING) { + SetPauseAction(PAUSE_CONTINUE, false); + actionComplete = true; + actionWait.notify_all(); + } } } // namespace diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index cb651af234..3e9b5086cc 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -20,6 +20,7 @@ #include #include "Common/CommonTypes.h" +#include "Core/Core.h" #include "GPU/Common/GPUDebugInterface.h" namespace GPUStepping { @@ -37,5 +38,5 @@ namespace GPUStepping { bool GPU_SetCmdValue(u32 op); void ResumeFromStepping(); - void ForceUnpause(); + void ForceUnpause(CoreLifecycle stage); }; diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 2b1639971f..e77238ba3b 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -67,7 +67,7 @@ void CGEDebugger::Init() { CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) { GPUBreakpoints::Init(); - Core_ListenShutdown(ForceUnpause); + Core_ListenLifecycle(ForceUnpause); // minimum size = a little more than the default RECT windowRect; From 0b8359fd77335bf68272a5c6f6380a58582df8d5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 16:28:29 -0700 Subject: [PATCH 120/301] Debugger: Add mutexing to disassembly manager. It was crashing when accessing from another thread. If in the future we go down to only the remote debugger, we could potentially remove this. --- Core/Debugger/DisassemblyManager.cpp | 49 +++++++++++++++++++++------- Core/Debugger/DisassemblyManager.h | 4 +++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 4ff4c58fd3..4631c9e036 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -31,6 +31,7 @@ #include "Core/Debugger/DisassemblyManager.h" std::map DisassemblyManager::entries; +std::recursive_mutex DisassemblyManager::entriesLock_; DebugInterface* DisassemblyManager::cpu; int DisassemblyManager::maxParamChars = 29; @@ -149,7 +150,8 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024) if (!PSP_IsInited()) return; - auto it = findDisassemblyEntry(entries,address,false); + std::lock_guard guard(entriesLock_); + auto it = findDisassemblyEntry(entries, address, false); if (it != entries.end()) { DisassemblyEntry* entry = it->second; @@ -219,7 +221,8 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024) std::vector DisassemblyManager::getBranchLines(u32 start, u32 size) { std::vector result; - + + std::lock_guard guard(entriesLock_); auto it = findDisassemblyEntry(entries,start,false); if (it != entries.end()) { @@ -235,6 +238,7 @@ std::vector DisassemblyManager::getBranchLines(u32 start, u32 size) void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest) { + std::lock_guard guard(entriesLock_); auto it = findDisassemblyEntry(entries,address,false); if (it == entries.end()) { @@ -267,6 +271,7 @@ void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLin u32 DisassemblyManager::getStartAddress(u32 address) { + std::lock_guard guard(entriesLock_); auto it = findDisassemblyEntry(entries,address,false); if (it == entries.end()) { @@ -283,6 +288,7 @@ u32 DisassemblyManager::getStartAddress(u32 address) u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n) { + std::lock_guard guard(entriesLock_); while (Memory::IsValidAddress(address)) { auto it = findDisassemblyEntry(entries,address,false); @@ -311,6 +317,7 @@ u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n) u32 DisassemblyManager::getNthNextAddress(u32 address, int n) { + std::lock_guard guard(entriesLock_); while (Memory::IsValidAddress(address)) { auto it = findDisassemblyEntry(entries,address,false); @@ -345,6 +352,7 @@ DisassemblyManager::~DisassemblyManager() { void DisassemblyManager::clear() { + std::lock_guard guard(entriesLock_); for (auto it = entries.begin(); it != entries.end(); it++) { delete it->second; @@ -383,11 +391,13 @@ void DisassemblyFunction::recheck() int DisassemblyFunction::getNumLines() { + std::lock_guard guard(lock_); return (int) lineAddresses.size(); } int DisassemblyFunction::getLineNum(u32 address, bool findStart) { + std::lock_guard guard(lock_); if (findStart) { int last = (int)lineAddresses.size() - 1; @@ -418,11 +428,13 @@ int DisassemblyFunction::getLineNum(u32 address, bool findStart) u32 DisassemblyFunction::getLineAddress(int line) { + std::lock_guard guard(lock_); return lineAddresses[line]; } bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) { + std::lock_guard guard(lock_); auto it = findDisassemblyEntry(entries,address,false); if (it == entries.end()) return false; @@ -434,6 +446,7 @@ void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector guard(lock_); for (size_t i = 0; i < lines.size(); i++) { BranchLine& line = lines[i]; @@ -466,6 +479,7 @@ void DisassemblyFunction::generateBranchLines() u32 end = address+size; + std::lock_guard guard(lock_); DebugInterface* cpu = DisassemblyManager::getCpu(); for (u32 funcPos = address; funcPos < end; funcPos += 4) { @@ -524,6 +538,7 @@ void DisassemblyFunction::generateBranchLines() void DisassemblyFunction::addOpcodeSequence(u32 start, u32 end) { DisassemblyOpcode* opcode = new DisassemblyOpcode(start,(end-start)/4); + std::lock_guard guard(lock_); entries[start] = opcode; for (u32 pos = start; pos < end; pos += 4) { @@ -537,18 +552,21 @@ void DisassemblyFunction::load() // gather all branch targets std::set branchTargets; - for (size_t i = 0; i < lines.size(); i++) { - switch (lines[i].type) + std::lock_guard guard(lock_); + for (size_t i = 0; i < lines.size(); i++) { - case LINE_DOWN: - branchTargets.insert(lines[i].second); - break; - case LINE_UP: - branchTargets.insert(lines[i].first); - break; - default: - break; + switch (lines[i].type) + { + case LINE_DOWN: + branchTargets.insert(lines[i].second); + break; + case LINE_UP: + branchTargets.insert(lines[i].first); + break; + default: + break; + } } } @@ -566,6 +584,7 @@ void DisassemblyFunction::load() addOpcodeSequence(opcodeSequenceStart,funcPos); DisassemblyData* data = new DisassemblyData(funcPos,g_symbolMap->GetDataSize(funcPos),g_symbolMap->GetDataType(funcPos)); + std::lock_guard guard(lock_); entries[funcPos] = data; lineAddresses.push_back(funcPos); funcPos += data->getTotalSize(); @@ -581,6 +600,7 @@ void DisassemblyFunction::load() u32 nextPos = (funcPos+3) & ~3; DisassemblyComment* comment = new DisassemblyComment(funcPos,nextPos-funcPos,".align","4"); + std::lock_guard guard(lock_); entries[funcPos] = comment; lineAddresses.push_back(funcPos); @@ -665,6 +685,7 @@ void DisassemblyFunction::load() if (opcodeSequenceStart != opAddress) addOpcodeSequence(opcodeSequenceStart,opAddress); + std::lock_guard guard(lock_); entries[opAddress] = macro; for (int i = 0; i < macro->getNumLines(); i++) { @@ -686,6 +707,7 @@ void DisassemblyFunction::load() void DisassemblyFunction::clear() { + std::lock_guard guard(lock_); for (auto it = entries.begin(); it != entries.end(); it++) { delete it->second; @@ -866,6 +888,7 @@ bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo& dest, bool i return false; } + std::lock_guard guard(lock_); auto it = lines.find(address); if (it == lines.end()) return false; @@ -877,6 +900,7 @@ bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo& dest, bool i int DisassemblyData::getLineNum(u32 address, bool findStart) { + std::lock_guard guard(lock_); auto it = lines.upper_bound(address); if (it != lines.end()) { @@ -891,6 +915,7 @@ int DisassemblyData::getLineNum(u32 address, bool findStart) void DisassemblyData::createLines() { + std::lock_guard guard(lock_); lines.clear(); lineAddresses.clear(); diff --git a/Core/Debugger/DisassemblyManager.h b/Core/Debugger/DisassemblyManager.h index 9002f1f8c1..7d84805b01 100644 --- a/Core/Debugger/DisassemblyManager.h +++ b/Core/Debugger/DisassemblyManager.h @@ -17,6 +17,7 @@ #pragma once +#include #include "Common/CommonTypes.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MIPS/MIPSAnalyst.h" @@ -91,6 +92,7 @@ private: std::vector lines; std::map entries; std::vector lineAddresses; + std::recursive_mutex lock_; }; class DisassemblyOpcode: public DisassemblyEntry @@ -169,6 +171,7 @@ private: DataType type; std::map lines; std::vector lineAddresses; + std::recursive_mutex lock_; }; class DisassemblyComment: public DisassemblyEntry @@ -214,6 +217,7 @@ public: static int getMaxParamChars() { return maxParamChars; }; private: static std::map entries; + static std::recursive_mutex entriesLock_; static DebugInterface* cpu; static int maxParamChars; }; From 0f84c2e9952c37c752a42133004303035e41e454 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 24 Apr 2018 12:25:41 -0700 Subject: [PATCH 121/301] Debugger: Consistently handle invalid addresses. --- Core/Debugger/DisassemblyManager.cpp | 33 ++++++++++++++-------------- Core/MIPS/MIPSAnalyst.cpp | 1 + 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 4631c9e036..77e8c917a5 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -244,29 +244,28 @@ void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLin { analyze(address); it = findDisassemblyEntry(entries,address,false); - - if (it == entries.end()) - { - if (address % 4) - dest.totalSize = ((address+3) & ~3)-address; - else - dest.totalSize = 4; - dest.name = "ERROR"; - dest.params = "Disassembly failure"; - return; - } } - DisassemblyEntry* entry = it->second; - if (entry->disassemble(address,dest,insertSymbols)) - return; - + if (it != entries.end()) { + DisassemblyEntry *entry = it->second; + if (entry->disassemble(address, dest, insertSymbols)) + return; + } + + dest.type = DISTYPE_OTHER; + memset(&dest.info, 0, sizeof(dest.info)); + dest.info.opcodeAddress = address; if (address % 4) dest.totalSize = ((address+3) & ~3)-address; else dest.totalSize = 4; - dest.name = "ERROR"; - dest.params = "Disassembly failure"; + if (Memory::IsValidRange(address, 4)) { + dest.name = "ERROR"; + dest.params = "Disassembly failure"; + } else { + dest.name = "-"; + dest.params = ""; + } } u32 DisassemblyManager::getStartAddress(u32 address) diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 569f49df5a..43e5019a66 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -1415,6 +1415,7 @@ skip: memset(&info, 0, sizeof(info)); if (!Memory::IsValidAddress(address)) { + info.opcodeAddress = address; return info; } From 6e027715913fd79a26bab275a7a268ec6b3019a4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 18:57:25 -0700 Subject: [PATCH 122/301] UI: Use UISTATE_PAUSEMENU while game still running. --- UI/EmuScreen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e68cf8b5d7..8064883ada 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -399,15 +399,15 @@ void EmuScreen::sendMessage(const char *message, const char *value) { // In case we need to position touch controls differently. RecreateViews(); } else if (!strcmp(message, "control mapping") && screenManager()->topScreen() == this) { - UpdateUIState(UISTATE_MENU); + UpdateUIState(UISTATE_PAUSEMENU); releaseButtons(); screenManager()->push(new ControlMappingScreen()); } else if (!strcmp(message, "display layout editor") && screenManager()->topScreen() == this) { - UpdateUIState(UISTATE_MENU); + UpdateUIState(UISTATE_PAUSEMENU); releaseButtons(); screenManager()->push(new DisplayLayoutScreen()); } else if (!strcmp(message, "settings") && screenManager()->topScreen() == this) { - UpdateUIState(UISTATE_MENU); + UpdateUIState(UISTATE_PAUSEMENU); releaseButtons(); screenManager()->push(new GameSettingsScreen(gamePath_)); } else if (!strcmp(message, "gpu dump next frame")) { From 6fbd0e27bb14759558f3bc684b777baa5a4aa108 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 2 May 2018 07:14:46 -0700 Subject: [PATCH 123/301] Core: Remove some unused coreState funcs. --- Core/Core.cpp | 17 +++-------------- Core/Core.h | 2 -- Core/CoreTiming.cpp | 2 +- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 722fed54d2..5bf6793764 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -28,22 +28,21 @@ #include "thread/threadutil.h" #include "profiler/profiler.h" +#include "Common/GraphicsContext.h" #include "Core/Core.h" #include "Core/Config.h" +#include "Core/Host.h" #include "Core/MemMap.h" #include "Core/SaveState.h" #include "Core/System.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/MIPS/MIPS.h" -#include "Common/GraphicsContext.h" #ifdef _WIN32 #include "Common/CommonWindows.h" #include "Windows/InputDevice.h" #endif -#include "Host.h" - -#include "Core/Debugger/Breakpoints.h" // Time until we stop considering the core active without user input. // Should this be configurable? 2 hours currently. @@ -86,16 +85,6 @@ void Core_NotifyLifecycle(CoreLifecycle stage) { } } -void Core_ErrorPause() { - Core_UpdateState(CORE_ERROR); -} - -void Core_Halt(const char *msg) { - Core_EnableStepping(true); - ERROR_LOG(CPU, "CPU HALTED : %s",msg); - _dbg_update_(); -} - void Core_Stop() { Core_UpdateState(CORE_POWERDOWN); m_StepCond.notify_all(); diff --git a/Core/Core.h b/Core/Core.h index fe881f2aa5..f01a9d03e8 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -27,7 +27,6 @@ void UpdateRunLoop(); void Core_Run(GraphicsContext *ctx); void Core_Stop(); -void Core_ErrorPause(); // For platforms that don't call Core_Run void Core_SetGraphicsContext(GraphicsContext *ctx); @@ -50,7 +49,6 @@ enum class CoreLifecycle { typedef void (* CoreLifecycleFunc)(CoreLifecycle stage); void Core_ListenLifecycle(CoreLifecycleFunc func); void Core_NotifyLifecycle(CoreLifecycle stage); -void Core_Halt(const char *msg); bool Core_IsStepping(); diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 78ca1d2fb8..b3f9a9587b 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -182,7 +182,7 @@ int RegisterEvent(const char *name, TimedCallback callback) void AntiCrashCallback(u64 userdata, int cyclesLate) { ERROR_LOG(SAVESTATE, "Savestate broken: an unregistered event was called."); - Core_Halt("invalid timing events"); + Core_EnableStepping(true); } void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) From ca4340b9e06067f36bbac9a7eee5f5319b62904e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 17:06:54 -0700 Subject: [PATCH 124/301] net: Support ipv6 lookups. We try to connect() to both, so should still be safe in environments where ipv6 is broken. --- UI/RemoteISOScreen.cpp | 16 +++++++++++++--- ext/native/net/http_client.cpp | 8 ++++---- ext/native/net/http_client.h | 3 ++- ext/native/net/http_server.h | 2 +- ext/native/net/resolve.cpp | 8 ++++++-- ext/native/net/resolve.h | 8 +++++++- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 2767e72761..b5ad54498c 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -69,11 +69,13 @@ static ServerStatus RetrieveStatus() { // relay that address to a mobile device searching for the server. static void RegisterServer(int port) { http::Client http; - Buffer theVoid; - if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) { + char resource[1024] = {}; + // We register both IPv4 and IPv6 in case the other client is using a different one. + // Our server is (currently?) ipv4 only, so we always register the ipv4 local for now. + if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) { + Buffer theVoid; if (http.Connect(2, 20.0, &scanCancelled)) { - char resource[1024] = {}; std::string ip = fd_util::GetLocalIP(http.sock()); snprintf(resource, sizeof(resource) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); @@ -81,6 +83,14 @@ static void RegisterServer(int port) { http.Disconnect(); } } + + if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) { + Buffer theVoid; + if (http.Connect()) { + http.GET(resource, &theVoid); + http.Disconnect(); + } + } } static void ExecuteServer() { diff --git a/ext/native/net/http_client.cpp b/ext/native/net/http_client.cpp index becb4e4473..3cb6967326 100644 --- a/ext/native/net/http_client.cpp +++ b/ext/native/net/http_client.cpp @@ -46,7 +46,7 @@ inline unsigned short myhtons(unsigned short x) { return (x >> 8) | (x << 8); } -bool Connection::Resolve(const char *host, int port) { +bool Connection::Resolve(const char *host, int port, DNSType type) { if ((intptr_t)sock_ != -1) { ELOG("Resolve: Already have a socket"); return false; @@ -63,7 +63,7 @@ bool Connection::Resolve(const char *host, int port) { snprintf(port_str, sizeof(port_str), "%d", port); std::string err; - if (!net::DNSResolve(host, port_str, &resolved_, err)) { + if (!net::DNSResolve(host, port_str, &resolved_, err, type)) { ELOG("Failed to resolve host %s: %s", host, err.c_str()); // So that future calls fail. port_ = 0; @@ -87,10 +87,10 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) { FD_ZERO(&fds); for (addrinfo *possible = resolved_; possible != nullptr; possible = possible->ai_next) { // TODO: Could support ipv6 without huge difficulty... - if (possible->ai_family != AF_INET) + if (possible->ai_family != AF_INET && possible->ai_family != AF_INET6) continue; - int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + int sock = socket(possible->ai_family, SOCK_STREAM, IPPROTO_TCP); if ((intptr_t)sock == -1) { ELOG("Bad socket"); continue; diff --git a/ext/native/net/http_client.h b/ext/native/net/http_client.h index 0f63c4bb9c..ac3d26ed97 100644 --- a/ext/native/net/http_client.h +++ b/ext/native/net/http_client.h @@ -6,6 +6,7 @@ #include "base/basictypes.h" #include "base/buffer.h" +#include "net/resolve.h" #ifdef _WIN32 #ifndef NOMINMAX @@ -28,7 +29,7 @@ public: virtual ~Connection(); // Inits the sockaddr_in. - bool Resolve(const char *host, int port); + bool Resolve(const char *host, int port, DNSType type = DNSType::ANY); bool Connect(int maxTries = 2, double timeout = 20.0f, bool *cancelConnect = nullptr); void Disconnect(); diff --git a/ext/native/net/http_server.h b/ext/native/net/http_server.h index 974b45ee6d..29ba802618 100644 --- a/ext/native/net/http_server.h +++ b/ext/native/net/http_server.h @@ -85,7 +85,7 @@ public: virtual void HandleRequest(const Request &request); int Port() { - return port_; + return port_; } private: diff --git a/ext/native/net/resolve.cpp b/ext/native/net/resolve.cpp index 1876427b14..ec7d17f1d1 100644 --- a/ext/native/net/resolve.cpp +++ b/ext/native/net/resolve.cpp @@ -77,7 +77,7 @@ char *DNSResolve(const char *host) return ip; } -bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error) +bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type) { addrinfo hints = {0}; // TODO: Might be uses to lookup other values. @@ -89,7 +89,11 @@ bool DNSResolve(const std::string &host, const std::string &service, addrinfo ** // http://stackoverflow.com/questions/1408030/what-is-the-purpose-of-the-ai-v4mapped-flag-in-getaddrinfo hints.ai_flags = /*AI_V4MAPPED |*/ AI_ADDRCONFIG; #endif - hints.ai_protocol = IPPROTO_TCP; + hints.ai_protocol = 0; + if (type == DNSType::IPV4) + hints.ai_family = AF_INET; + else if (type == DNSType::IPV6) + hints.ai_family = AF_INET6; const char *servicep = service.length() == 0 ? NULL : service.c_str(); diff --git a/ext/native/net/resolve.h b/ext/native/net/resolve.h index 46a8b3e598..58ce0984d9 100644 --- a/ext/native/net/resolve.h +++ b/ext/native/net/resolve.h @@ -14,7 +14,13 @@ void Shutdown(); char *DNSResolveTry(const char *host, const char **err); char *DNSResolve(const char *host); -bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error); +enum class DNSType { + ANY = 0, + IPV4 = 1, + IPV6 = 2, +}; + +bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type = DNSType::ANY); void DNSResolveFree(addrinfo *res); int inet_pton(int af, const char* src, void* dst); From 30a07c2e8d23cbef24b841e7c2ad15ae9e80aa41 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 17:09:36 -0700 Subject: [PATCH 125/301] net: Support local ipv6 formatting. Let's hope no platform has issues with inet_ntop... --- ext/native/file/fd_util.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ext/native/file/fd_util.cpp b/ext/native/file/fd_util.cpp index 8663d44760..b61703c263 100644 --- a/ext/native/file/fd_util.cpp +++ b/ext/native/file/fd_util.cpp @@ -133,11 +133,22 @@ void SetNonBlocking(int sock, bool non_blocking) { } std::string GetLocalIP(int sock) { - struct sockaddr_in server_addr; + union { + struct sockaddr sa; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } server_addr; memset(&server_addr, 0, sizeof(server_addr)); socklen_t len = sizeof(server_addr); if (getsockname(sock, (struct sockaddr *)&server_addr, &len) == 0) { - char *result = inet_ntoa(*(in_addr *)&server_addr.sin_addr); + char temp[64]; + void *addr; + if (server_addr.sa.sa_family == AF_INET6) { + addr = &server_addr.ipv6.sin6_addr; + } else { + addr = &server_addr.ipv4.sin_addr; + } + const char *result = inet_ntop(server_addr.sa.sa_family, addr, temp, sizeof(temp)); if (result) { return result; } From 5b95de663e6cee3b84c844790e96348017970bb5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 5 May 2018 22:52:57 -0700 Subject: [PATCH 126/301] net: Listen on ipv6 and ipv4. Hurray, no longer "part of the problem" for ipv4. --- UI/RemoteISOScreen.cpp | 27 +++++++++---- ext/native/net/http_server.cpp | 74 +++++++++++++++++++++++++++++++--- ext/native/net/http_server.h | 6 ++- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index b5ad54498c..774946905a 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -69,25 +69,36 @@ static ServerStatus RetrieveStatus() { // relay that address to a mobile device searching for the server. static void RegisterServer(int port) { http::Client http; + Buffer theVoid; - char resource[1024] = {}; - // We register both IPv4 and IPv6 in case the other client is using a different one. - // Our server is (currently?) ipv4 only, so we always register the ipv4 local for now. + char resource4[1024] = {}; if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) { - Buffer theVoid; if (http.Connect(2, 20.0, &scanCancelled)) { std::string ip = fd_util::GetLocalIP(http.sock()); - snprintf(resource, sizeof(resource) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); + snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - http.GET(resource, &theVoid); + http.GET(resource4, &theVoid); + theVoid.Skip(theVoid.size()); http.Disconnect(); } } if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) { - Buffer theVoid; + // We register both IPv4 and IPv6 in case the other client is using a different one. + if (resource4[0] != 0 && http.Connect()) { + http.GET(resource4, &theVoid); + theVoid.Skip(theVoid.size()); + http.Disconnect(); + } + + // Currently, we're not using keepalive, so gotta reconnect... if (http.Connect()) { - http.GET(resource, &theVoid); + char resource6[1024] = {}; + std::string ip = fd_util::GetLocalIP(http.sock()); + snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); + + http.GET(resource6, &theVoid); + theVoid.Skip(theVoid.size()); http.Disconnect(); } } diff --git a/ext/native/net/http_server.cpp b/ext/native/net/http_server.cpp index 9357d2fe69..ddd893e5ac 100644 --- a/ext/native/net/http_server.cpp +++ b/ext/native/net/http_server.cpp @@ -130,9 +130,21 @@ void Server::SetFallbackHandler(UrlHandlerFunc handler) { fallback_ = handler; } -bool Server::Listen(int port) { +bool Server::Listen(int port, net::DNSType type) { + bool success = false; + if (type == net::DNSType::ANY || type == net::DNSType::IPV6) { + success = Listen6(port, type == net::DNSType::IPV6); + } + if (!success && (type == net::DNSType::ANY || type == net::DNSType::IPV4)) { + success = Listen4(port); + } + return success; +} + +bool Server::Listen4(int port) { listener_ = socket(AF_INET, SOCK_STREAM, 0); - CHECK_GE(listener_, 0); + if (listener_ < 0) + return false; struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); @@ -145,6 +157,7 @@ bool Server::Listen(int port) { setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + closesocket(listener_); ELOG("Failed to bind to port %i. Bailing.", port); return false; } @@ -152,7 +165,10 @@ bool Server::Listen(int port) { fd_util::SetNonBlocking(listener_, true); // 1024 is the max number of queued requests. - CHECK_GE(listen(listener_, 1024), 0); + if (listen(listener_, 1024) < 0) { + closesocket(listener_); + return false; + } socklen_t len = sizeof(server_addr); if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) { @@ -165,6 +181,50 @@ bool Server::Listen(int port) { return true; } +bool Server::Listen6(int port, bool ipv6_only) { + listener_ = socket(AF_INET6, SOCK_STREAM, 0); + if (listener_ < 0) + return false; + + struct sockaddr_in6 server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin6_family = AF_INET6; + server_addr.sin6_addr = in6addr_any; + server_addr.sin6_port = htons(port); + + int opt = 1; + // Enable re-binding to avoid the pain when restarting the server quickly. + setsockopt(listener_, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); + + // Enable listening on IPv6 and IPv4? + opt = ipv6_only ? 1 : 0; + setsockopt(listener_, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt)); + + if (bind(listener_, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + closesocket(listener_); + ELOG("Failed to bind to port %i. Bailing.", port); + return false; + } + + fd_util::SetNonBlocking(listener_, true); + + // 1024 is the max number of queued requests. + if (listen(listener_, 1024) < 0) { + closesocket(listener_); + return false; + } + + socklen_t len = sizeof(server_addr); + if (getsockname(listener_, (struct sockaddr *)&server_addr, &len) == 0) { + port = ntohs(server_addr.sin6_port); + } + + ILOG("HTTP server started on port %i", port); + port_ = port; + + return true; +} + bool Server::RunSlice(double timeout) { if (listener_ < 0 || port_ == 0) { return false; @@ -177,9 +237,13 @@ bool Server::RunSlice(double timeout) { return false; } - sockaddr client_addr; + union { + struct sockaddr sa; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } client_addr; socklen_t client_addr_size = sizeof(client_addr); - int conn_fd = accept(listener_, &client_addr, &client_addr_size); + int conn_fd = accept(listener_, &client_addr.sa, &client_addr_size); if (conn_fd >= 0) { executor_->Run(std::bind(&Server::HandleConnection, this, conn_fd)); return true; diff --git a/ext/native/net/http_server.h b/ext/native/net/http_server.h index 29ba802618..5220bda762 100644 --- a/ext/native/net/http_server.h +++ b/ext/native/net/http_server.h @@ -6,6 +6,7 @@ #include "base/buffer.h" #include "net/http_headers.h" +#include "net/resolve.h" #include "thread/executor.h" namespace net { @@ -73,7 +74,7 @@ public: // May run for (significantly) longer than timeout, but won't wait longer than that // for a new connection to handle. bool RunSlice(double timeout); - bool Listen(int port); + bool Listen(int port, net::DNSType type = net::DNSType::ANY); void Stop(); void RegisterHandler(const char *url_path, UrlHandlerFunc handler); @@ -89,6 +90,9 @@ public: } private: + bool Listen6(int port, bool ipv6_only); + bool Listen4(int port); + void HandleConnection(int conn_fd); // Things like default 404, etc. From 182990217127a38e2f596669b9eab3fdff9feeca Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 26 May 2018 13:03:36 -0700 Subject: [PATCH 127/301] GE Debugger: Show bezier/spline in preview. Ignoring some things about normals and colors since they don't matter. --- GPU/Common/SplineCommon.h | 2 +- GPU/GeDisasm.cpp | 4 +- Windows/GEDebugger/TabVertices.cpp | 4 + Windows/GEDebugger/VertexPreview.cpp | 167 ++++++++++++++++++++++++++- 4 files changed, 169 insertions(+), 8 deletions(-) diff --git a/GPU/Common/SplineCommon.h b/GPU/Common/SplineCommon.h index e292c2b937..1cddb1ddb2 100644 --- a/GPU/Common/SplineCommon.h +++ b/GPU/Common/SplineCommon.h @@ -67,4 +67,4 @@ enum SplineQuality { }; void TessellateSplinePatch(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch, u32 origVertType, int maxVertices); -void TessellateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType, int maxVertices); +void TessellateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType); diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index 8acb1ed4ea..d334385691 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -162,9 +162,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { int sp_utype = (data >> 16) & 0x3; int sp_vtype = (data >> 18) & 0x3; if (data & 0xF00000) - snprintf(buffer, bufsize, "DRAW SPLINE: %i x %i, %i x %i (extra %x)", sp_ucount, sp_vcount, sp_utype, sp_vtype, data >> 20); + snprintf(buffer, bufsize, "DRAW SPLINE: %i x %i (type %ix%i, extra %x)", sp_ucount, sp_vcount, sp_utype, sp_vtype, data >> 20); else - snprintf(buffer, bufsize, "DRAW SPLINE: %i x %i, %i x %i", sp_ucount, sp_vcount, sp_utype, sp_vtype); + snprintf(buffer, bufsize, "DRAW SPLINE: %i x %i (type %ix%i)", sp_ucount, sp_vcount, sp_utype, sp_vtype); } break; diff --git a/Windows/GEDebugger/TabVertices.cpp b/Windows/GEDebugger/TabVertices.cpp index f3888669d6..e878105fdd 100644 --- a/Windows/GEDebugger/TabVertices.cpp +++ b/Windows/GEDebugger/TabVertices.cpp @@ -281,6 +281,10 @@ int CtrlVertexList::GetRowCount() { u32 cmd = Memory::Read_U32(list.pc); if ((cmd >> 24) == GE_CMD_PRIM) { rowCount_ = cmd & 0xFFFF; + } else if ((cmd >> 24) == GE_CMD_BEZIER || (cmd >> 24) == GE_CMD_SPLINE) { + u32 u = (cmd & 0x00FF) >> 0; + u32 v = (cmd & 0xFF00) >> 8; + rowCount_ = u * v; } } diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index 1f78554d91..8714aa05e3 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -24,6 +24,7 @@ #include "Core/Config.h" #include "GPU/GPUInterface.h" #include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Common/SplineCommon.h" #include "GPU/GPUState.h" static const char preview_fs[] = @@ -153,11 +154,10 @@ static void ExpandRectangles(std::vector &vertices, std::vector< u32 CGEDebugger::PrimPreviewOp() { DisplayList list; - if (gpuDebug != nullptr && gpuDebug->GetCurrentDisplayList(list)) { + if (gpuDebug != nullptr && gpuDebug->GetCurrentDisplayList(list) && !showClut_) { const u32 op = Memory::Read_U32(list.pc); const u32 cmd = op >> 24; - // TODO: Bezier/spline? - if (cmd == GE_CMD_PRIM && !showClut_) { + if (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE) { return op; } } @@ -165,9 +165,160 @@ u32 CGEDebugger::PrimPreviewOp() { return 0; } +static void ExpandBezier(int &count, int op, std::vector &vertices, std::vector &indices) { + int count_u = (op & 0x00FF) >> 0; + int count_v = (op & 0xFF00) >> 8; + + int tess_u = gstate.getPatchDivisionU(); + int tess_v = gstate.getPatchDivisionV(); + if (tess_u < 1) { + tess_u = 1; + } + if (tess_v < 1) { + tess_v = 1; + } + + std::vector simpleVerts; + simpleVerts.resize(vertices.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + // For now, let's just copy back so we can use TessellateBezierPatch... + simpleVerts[i].uv[0] = vertices[i].u; + simpleVerts[i].uv[1] = vertices[i].v; + simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z); + } + + // Bezier patches share less control points than spline patches. Otherwise they are pretty much the same (except bezier don't support the open/close thing) + int num_patches_u = (count_u - 1) / 3; + int num_patches_v = (count_v - 1) / 3; + int total_patches = num_patches_u * num_patches_v; + std::vector patches; + patches.resize(total_patches); + for (int patch_u = 0; patch_u < num_patches_u; patch_u++) { + for (int patch_v = 0; patch_v < num_patches_v; patch_v++) { + BezierPatch &patch = patches[patch_u + patch_v * num_patches_u]; + for (int point = 0; point < 16; ++point) { + int idx = (patch_u * 3 + point % 4) + (patch_v * 3 + point / 4) * count_u; + patch.points[point] = &simpleVerts[0] + (!indices.empty() ? indices[idx] : idx); + } + patch.u_index = patch_u * 3; + patch.v_index = patch_v * 3; + patch.index = patch_v * num_patches_u + patch_u; + patch.primType = gstate.getPatchPrimitiveType(); + patch.computeNormals = false; + patch.patchFacing = false; + } + } + + std::vector generatedVerts; + std::vector generatedInds; + generatedVerts.resize((tess_u + 1) * (tess_v + 1) * total_patches); + generatedInds.resize(tess_u * tess_v * 6); + + count = 0; + u8 *dest = (u8 *)&generatedVerts[0]; + u16 *inds = &generatedInds[0]; + for (int patch_idx = 0; patch_idx < total_patches; ++patch_idx) { + const BezierPatch &patch = patches[patch_idx]; + TessellateBezierPatch(dest, inds, count, tess_u, tess_v, patch, gstate.vertType); + } + + vertices.resize(generatedVerts.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].u = generatedVerts[i].uv[0]; + vertices[i].v = generatedVerts[i].uv[1]; + vertices[i].x = generatedVerts[i].pos.x; + vertices[i].y = generatedVerts[i].pos.y; + vertices[i].z = generatedVerts[i].pos.z; + } + indices = generatedInds; +} + +static void ExpandSpline(int &count, int op, std::vector &vertices, std::vector &indices) { + SplinePatchLocal patch; + patch.computeNormals = false; + patch.primType = gstate.getPatchPrimitiveType(); + patch.patchFacing = false; + + patch.count_u = (op & 0x00FF) >> 0; + patch.count_v = (op & 0xFF00) >> 8; + patch.type_u = (op >> 16) & 0x3; + patch.type_v = (op >> 18) & 0x3; + + patch.tess_u = gstate.getPatchDivisionU(); + patch.tess_v = gstate.getPatchDivisionV(); + if (patch.tess_u < 1) { + patch.tess_u = 1; + } + if (patch.tess_v < 1) { + patch.tess_v = 1; + } + + // Real hardware seems to draw nothing when given < 4 either U or V. + if (patch.count_u < 4 || patch.count_v < 4) { + return; + } + + std::vector simpleVerts; + simpleVerts.resize(vertices.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + // For now, let's just copy back so we can use TessellateBezierPatch... + simpleVerts[i].uv[0] = vertices[i].u; + simpleVerts[i].uv[1] = vertices[i].v; + simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z); + } + + std::vector points; + points.resize(patch.count_u * patch.count_v); + + // Make an array of pointers to the control points, to get rid of indices. + for (int idx = 0; idx < patch.count_u * patch.count_v; idx++) { + points[idx] = &simpleVerts[0] + (!indices.empty() ? indices[idx] : idx); + } + patch.points = &points[0]; + + int patch_div_s = (patch.count_u - 3) * patch.tess_u; + int patch_div_t = (patch.count_v - 3) * patch.tess_v; + int maxVertexCount = (patch_div_s + 1) * (patch_div_t + 1); + + std::vector generatedVerts; + std::vector generatedInds; + generatedVerts.resize(maxVertexCount); + generatedInds.resize(patch_div_s * patch_div_t * 6); + + count = 0; + u8 *dest = (u8 *)&generatedVerts[0]; + TessellateSplinePatch(dest, &generatedInds[0], count, patch, gstate.vertType, maxVertexCount); + + vertices.resize(generatedVerts.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].u = generatedVerts[i].uv[0]; + vertices[i].v = generatedVerts[i].uv[1]; + vertices[i].x = generatedVerts[i].pos.x; + vertices[i].y = generatedVerts[i].pos.y; + vertices[i].z = generatedVerts[i].pos.z; + } + indices = generatedInds; +} + void CGEDebugger::UpdatePrimPreview(u32 op, int which) { - const u32 prim_type = (op >> 16) & 0x7; - int count = op & 0xFFFF; + u32 prim_type = GE_PRIM_INVALID; + int count = 0; + int count_u = 0; + int count_v = 0; + + const u32 cmd = op >> 24; + if (cmd == GE_CMD_PRIM) { + prim_type = (op >> 16) & 0x7; + count = op & 0xFFFF; + } else { + const GEPrimitiveType primLookup[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS }; + if (gstate.getPatchPrimitiveType() < ARRAY_SIZE(primLookup)) + prim_type = primLookup[gstate.getPatchPrimitiveType()]; + count_u = (op & 0x00FF) >> 0; + count_v = (op & 0xFF00) >> 8; + count = count_u * count_v; + } + if (prim_type >= 7) { ERROR_LOG(G3D, "Unsupported prim type: %x", op); return; @@ -190,6 +341,12 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) { return; } + if (cmd == GE_CMD_BEZIER) { + ExpandBezier(count, op, vertices, indices); + } else if (cmd == GE_CMD_SPLINE) { + ExpandSpline(count, op, vertices, indices); + } + if (prim == GE_PRIM_RECTANGLES) { ExpandRectangles(vertices, indices, count, gpuDebug->GetGState().isModeThrough()); } From 9aad4dc84806dbfac9fdd6fa04e7d97ceb5fd078 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 26 May 2018 13:14:17 -0700 Subject: [PATCH 128/301] GE Debugger: Cleaned repetition in spline preview. --- GPU/Common/SplineCommon.cpp | 10 ++-- GPU/Common/SplineCommon.h | 4 +- Windows/GEDebugger/VertexPreview.cpp | 80 +++++++++++----------------- 3 files changed, 38 insertions(+), 56 deletions(-) diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 41eb4aec93..948c31b1f2 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -79,7 +79,7 @@ inline __m128 SSENormalizeMultiplier(bool useSSE4, __m128 v) -static void CopyQuad(u8 *&dest, const SimpleVertex *v1, const SimpleVertex *v2, const SimpleVertex* v3, const SimpleVertex *v4) { +static void CopyQuad(u8 *&dest, const SimpleVertex *v1, const SimpleVertex *v2, const SimpleVertex *v3, const SimpleVertex *v4) { int vertexSize = sizeof(SimpleVertex); memcpy(dest, v1, vertexSize); dest += vertexSize; @@ -458,7 +458,7 @@ static void SplinePatchFullQuality(u8 *&dest, u16 *indices, int &count, const Sp OutputDebugStringA(temp); Crash(); }*/ - SimpleVertex *a = spatch.points[idx]; + const SimpleVertex *a = spatch.points[idx]; AccumulateWeighted(vert_pos, a->pos, fv); if (origTc) { vert->uv[0] += a->uv[0] * f; @@ -877,7 +877,7 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi } // TODO: Do something less idiotic to manage this buffer - SimpleVertex **points = new SimpleVertex *[count_u * count_v]; + auto points = new const SimpleVertex *[count_u * count_v]; // Make an array of pointers to the control points, to get rid of indices. for (int idx = 0; idx < count_u * count_v; idx++) { @@ -1013,7 +1013,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi float *t = tex; float *c = col; for (int idx = 0; idx < count_u * count_v; idx++) { - SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(idx) : idx); + const SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(idx) : idx); memcpy(p, point->pos.AsArray(), 3 * sizeof(float)); p += posStride; if (hasTexCoords) { @@ -1026,7 +1026,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi } } if (!hasColor) { - SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(0) : 0); + const SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(0) : 0); memcpy(col, Vec4f::FromRGBA(point->color_32).AsArray(), 4 * sizeof(float)); } } else { diff --git a/GPU/Common/SplineCommon.h b/GPU/Common/SplineCommon.h index 1cddb1ddb2..7291f5bc9f 100644 --- a/GPU/Common/SplineCommon.h +++ b/GPU/Common/SplineCommon.h @@ -36,7 +36,7 @@ struct SimpleVertex { // We decode all vertices into a common format for easy interpolation and stuff. // Not fast but can be optimized later. struct BezierPatch { - SimpleVertex *points[16]; + const SimpleVertex *points[16]; // These are used to generate UVs. int u_index, v_index; @@ -48,7 +48,7 @@ struct BezierPatch { }; struct SplinePatchLocal { - SimpleVertex **points; + const SimpleVertex **points; int tess_u; int tess_v; int count_u; diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index 8714aa05e3..6eeef756dc 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -165,7 +165,7 @@ u32 CGEDebugger::PrimPreviewOp() { return 0; } -static void ExpandBezier(int &count, int op, std::vector &vertices, std::vector &indices) { +static void ExpandBezier(int &count, int op, const std::vector &simpleVerts, const std::vector &indices, std::vector &generatedVerts, std::vector &generatedInds) { int count_u = (op & 0x00FF) >> 0; int count_v = (op & 0xFF00) >> 8; @@ -178,15 +178,6 @@ static void ExpandBezier(int &count, int op, std::vector &vertic tess_v = 1; } - std::vector simpleVerts; - simpleVerts.resize(vertices.size()); - for (size_t i = 0; i < vertices.size(); ++i) { - // For now, let's just copy back so we can use TessellateBezierPatch... - simpleVerts[i].uv[0] = vertices[i].u; - simpleVerts[i].uv[1] = vertices[i].v; - simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z); - } - // Bezier patches share less control points than spline patches. Otherwise they are pretty much the same (except bezier don't support the open/close thing) int num_patches_u = (count_u - 1) / 3; int num_patches_v = (count_v - 1) / 3; @@ -209,8 +200,6 @@ static void ExpandBezier(int &count, int op, std::vector &vertic } } - std::vector generatedVerts; - std::vector generatedInds; generatedVerts.resize((tess_u + 1) * (tess_v + 1) * total_patches); generatedInds.resize(tess_u * tess_v * 6); @@ -221,19 +210,9 @@ static void ExpandBezier(int &count, int op, std::vector &vertic const BezierPatch &patch = patches[patch_idx]; TessellateBezierPatch(dest, inds, count, tess_u, tess_v, patch, gstate.vertType); } - - vertices.resize(generatedVerts.size()); - for (size_t i = 0; i < vertices.size(); ++i) { - vertices[i].u = generatedVerts[i].uv[0]; - vertices[i].v = generatedVerts[i].uv[1]; - vertices[i].x = generatedVerts[i].pos.x; - vertices[i].y = generatedVerts[i].pos.y; - vertices[i].z = generatedVerts[i].pos.z; - } - indices = generatedInds; } -static void ExpandSpline(int &count, int op, std::vector &vertices, std::vector &indices) { +static void ExpandSpline(int &count, int op, const std::vector &simpleVerts, const std::vector &indices, std::vector &generatedVerts, std::vector &generatedInds) { SplinePatchLocal patch; patch.computeNormals = false; patch.primType = gstate.getPatchPrimitiveType(); @@ -258,16 +237,7 @@ static void ExpandSpline(int &count, int op, std::vector &vertic return; } - std::vector simpleVerts; - simpleVerts.resize(vertices.size()); - for (size_t i = 0; i < vertices.size(); ++i) { - // For now, let's just copy back so we can use TessellateBezierPatch... - simpleVerts[i].uv[0] = vertices[i].u; - simpleVerts[i].uv[1] = vertices[i].v; - simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z); - } - - std::vector points; + std::vector points; points.resize(patch.count_u * patch.count_v); // Make an array of pointers to the control points, to get rid of indices. @@ -280,24 +250,12 @@ static void ExpandSpline(int &count, int op, std::vector &vertic int patch_div_t = (patch.count_v - 3) * patch.tess_v; int maxVertexCount = (patch_div_s + 1) * (patch_div_t + 1); - std::vector generatedVerts; - std::vector generatedInds; generatedVerts.resize(maxVertexCount); generatedInds.resize(patch_div_s * patch_div_t * 6); count = 0; u8 *dest = (u8 *)&generatedVerts[0]; TessellateSplinePatch(dest, &generatedInds[0], count, patch, gstate.vertType, maxVertexCount); - - vertices.resize(generatedVerts.size()); - for (size_t i = 0; i < vertices.size(); ++i) { - vertices[i].u = generatedVerts[i].uv[0]; - vertices[i].v = generatedVerts[i].uv[1]; - vertices[i].x = generatedVerts[i].pos.x; - vertices[i].y = generatedVerts[i].pos.y; - vertices[i].z = generatedVerts[i].pos.z; - } - indices = generatedInds; } void CGEDebugger::UpdatePrimPreview(u32 op, int which) { @@ -341,10 +299,34 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) { return; } - if (cmd == GE_CMD_BEZIER) { - ExpandBezier(count, op, vertices, indices); - } else if (cmd == GE_CMD_SPLINE) { - ExpandSpline(count, op, vertices, indices); + if (cmd != GE_CMD_PRIM) { + static std::vector generatedVerts; + static std::vector generatedInds; + + static std::vector simpleVerts; + simpleVerts.resize(vertices.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + // For now, let's just copy back so we can use TessellateBezierPatch/TessellateSplinePatch... + simpleVerts[i].uv[0] = vertices[i].u; + simpleVerts[i].uv[1] = vertices[i].v; + simpleVerts[i].pos = Vec3Packedf(vertices[i].x, vertices[i].y, vertices[i].z); + } + + if (cmd == GE_CMD_BEZIER) { + ExpandBezier(count, op, simpleVerts, indices, generatedVerts, generatedInds); + } else if (cmd == GE_CMD_SPLINE) { + ExpandSpline(count, op, simpleVerts, indices, generatedVerts, generatedInds); + } + + vertices.resize(generatedVerts.size()); + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].u = generatedVerts[i].uv[0]; + vertices[i].v = generatedVerts[i].uv[1]; + vertices[i].x = generatedVerts[i].pos.x; + vertices[i].y = generatedVerts[i].pos.y; + vertices[i].z = generatedVerts[i].pos.z; + } + indices = generatedInds; } if (prim == GE_PRIM_RECTANGLES) { From e21753bc93f1cb1412212a27259850512949d198 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 1 May 2018 20:17:12 -0700 Subject: [PATCH 129/301] Debugger: Add assembler to build. --- CMakeLists.txt | 4 -- Core/ELF/ElfReader.cpp | 2 +- Core/MIPS/MIPSAsm.cpp | 15 ----- android/jni/Android.mk | 110 ++++++++++++++++++---------------- ext/native/util/text/utf8.cpp | 14 +++++ ext/native/util/text/utf8.h | 4 ++ 6 files changed, 76 insertions(+), 73 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e26ebe2227..4d11a8b129 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1111,10 +1111,6 @@ include_directories(ext/xxhash) set(CoreExtra) set(CoreExtraLibs) -if(WIN32 OR ANDROID) - set(CoreExtraLibs ${CoreExtraLibs} armips) -endif() - set(CoreExtra ${CoreExtra} Core/MIPS/IR/IRCompALU.cpp Core/MIPS/IR/IRCompBranch.cpp diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 68f7531e2d..942236c68a 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -18,7 +18,7 @@ #include "Core/MemMap.h" #include "Core/Reporting.h" #include "Core/MIPS/MIPSTables.h" -#include "ElfReader.h" +#include "Core/ELF/ElfReader.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/HLE/sceKernelMemory.h" diff --git a/Core/MIPS/MIPSAsm.cpp b/Core/MIPS/MIPSAsm.cpp index 1defcb9477..6265db2390 100644 --- a/Core/MIPS/MIPSAsm.cpp +++ b/Core/MIPS/MIPSAsm.cpp @@ -7,16 +7,8 @@ #include "Common/CommonTypes.h" -#if defined(_WIN32) || defined(__ANDROID__) -// Temporarily turned off on Android -#define USE_ARMIPS -#endif - - -#ifdef USE_ARMIPS // This has to be before basictypes to avoid a define conflict. #include "ext/armips/Core/Assembler.h" -#endif #include "util/text/utf8.h" #include "Core/Debugger/SymbolMap.h" @@ -33,7 +25,6 @@ std::wstring GetAssembleError() return errorText; } -#ifdef USE_ARMIPS class PspAssemblerFile: public AssemblerFile { public: @@ -72,11 +63,9 @@ private: u64 address; std::wstring dummyWFilename_; }; -#endif bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address) { -#ifdef USE_ARMIPS PspAssemblerFile file; StringList errors; @@ -108,10 +97,6 @@ bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address) } return true; -#else - errorText = L"Unsupported platform"; - return false; -#endif } } // namespace diff --git a/android/jni/Android.mk b/android/jni/Android.mk index fc04485bb6..2c081f2ca7 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -161,6 +161,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/MIPS/MIPSAnalyst.cpp \ $(SRC)/Core/MIPS/MIPSDis.cpp \ $(SRC)/Core/MIPS/MIPSDisVFPU.cpp \ + $(SRC)/Core/MIPS/MIPSAsm.cpp \ $(SRC)/Core/MIPS/MIPSInt.cpp.arm \ $(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \ $(SRC)/Core/MIPS/MIPSStackWalk.cpp \ @@ -397,12 +398,64 @@ EXEC_AND_LIB_FILES := \ LOCAL_MODULE := ppsspp_core LOCAL_SRC_FILES := $(EXEC_AND_LIB_FILES) - include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) include $(LOCAL_PATH)/Locals.mk -LOCAL_STATIC_LIBRARIES += ppsspp_core +LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SRC)/ext/armips $(LOCAL_C_INCLUDES) + +LIBARMIPS_FILES := \ + $(SRC)/ext/armips/Archs/ARM/Arm.cpp \ + $(SRC)/ext/armips/Archs/ARM/ArmOpcodes.cpp \ + $(SRC)/ext/armips/Archs/ARM/ArmParser.cpp \ + $(SRC)/ext/armips/Archs/ARM/ArmRelocator.cpp \ + $(SRC)/ext/armips/Archs/ARM/CArmInstruction.cpp \ + $(SRC)/ext/armips/Archs/ARM/CThumbInstruction.cpp \ + $(SRC)/ext/armips/Archs/ARM/Pool.cpp \ + $(SRC)/ext/armips/Archs/ARM/ThumbOpcodes.cpp \ + $(SRC)/ext/armips/Archs/MIPS/CMipsInstruction.cpp \ + $(SRC)/ext/armips/Archs/MIPS/Mips.cpp \ + $(SRC)/ext/armips/Archs/MIPS/MipsElfFile.cpp \ + $(SRC)/ext/armips/Archs/MIPS/MipsMacros.cpp \ + $(SRC)/ext/armips/Archs/MIPS/MipsOpcodes.cpp \ + $(SRC)/ext/armips/Archs/MIPS/MipsParser.cpp \ + $(SRC)/ext/armips/Archs/MIPS/PsxRelocator.cpp \ + $(SRC)/ext/armips/Archs/Architecture.cpp \ + $(SRC)/ext/armips/Commands/CAssemblerCommand.cpp \ + $(SRC)/ext/armips/Commands/CAssemblerLabel.cpp \ + $(SRC)/ext/armips/Commands/CDirectiveArea.cpp \ + $(SRC)/ext/armips/Commands/CDirectiveConditional.cpp \ + $(SRC)/ext/armips/Commands/CDirectiveData.cpp \ + $(SRC)/ext/armips/Commands/CDirectiveFile.cpp \ + $(SRC)/ext/armips/Commands/CDirectiveMessage.cpp \ + $(SRC)/ext/armips/Commands/CommandSequence.cpp \ + $(SRC)/ext/armips/Core/ELF/ElfFile.cpp \ + $(SRC)/ext/armips/Core/ELF/ElfRelocator.cpp \ + $(SRC)/ext/armips/Core/Assembler.cpp \ + $(SRC)/ext/armips/Core/Common.cpp \ + $(SRC)/ext/armips/Core/Expression.cpp \ + $(SRC)/ext/armips/Core/ExpressionFunctions.cpp \ + $(SRC)/ext/armips/Core/FileManager.cpp \ + $(SRC)/ext/armips/Core/Misc.cpp \ + $(SRC)/ext/armips/Core/SymbolData.cpp \ + $(SRC)/ext/armips/Core/SymbolTable.cpp \ + $(SRC)/ext/armips/Parser/DirectivesParser.cpp \ + $(SRC)/ext/armips/Parser/ExpressionParser.cpp \ + $(SRC)/ext/armips/Parser/Parser.cpp \ + $(SRC)/ext/armips/Parser/Tokenizer.cpp \ + $(SRC)/ext/armips/Util/ByteArray.cpp \ + $(SRC)/ext/armips/Util/CRC.cpp \ + $(SRC)/ext/armips/Util/EncodingTable.cpp \ + $(SRC)/ext/armips/Util/FileClasses.cpp \ + $(SRC)/ext/armips/Util/Util.cpp + +LOCAL_MODULE := libarmips +LOCAL_SRC_FILES := $(LIBARMIPS_FILES) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +include $(LOCAL_PATH)/Locals.mk +LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips # These are the files just for ppsspp_jni LOCAL_MODULE := ppsspp_jni @@ -450,7 +503,7 @@ endif ifeq ($(HEADLESS),1) include $(CLEAR_VARS) include $(LOCAL_PATH)/Locals.mk - LOCAL_STATIC_LIBRARIES += ppsspp_core + LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips # Android 5.0 requires PIE for executables. Only supported on 4.1+, but this is testing anyway. LOCAL_CFLAGS += -fPIE @@ -468,59 +521,12 @@ endif ifeq ($(UNITTEST),1) include $(CLEAR_VARS) include $(LOCAL_PATH)/Locals.mk - LOCAL_STATIC_LIBRARIES += ppsspp_core + LOCAL_STATIC_LIBRARIES += ppsspp_core libarmips # Android 5.0 requires PIE for executables. Only supported on 4.1+, but this is testing anyway. LOCAL_CFLAGS += -fPIE LOCAL_LDFLAGS += -fPIE -pie - LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SRC)/ext/armips $(LOCAL_C_INCLUDES) - - LIBARMIPS_FILES := \ - $(SRC)/ext/armips/Archs/ARM/Arm.cpp \ - $(SRC)/ext/armips/Archs/ARM/ArmOpcodes.cpp \ - $(SRC)/ext/armips/Archs/ARM/ArmParser.cpp \ - $(SRC)/ext/armips/Archs/ARM/ArmRelocator.cpp \ - $(SRC)/ext/armips/Archs/ARM/CArmInstruction.cpp \ - $(SRC)/ext/armips/Archs/ARM/CThumbInstruction.cpp \ - $(SRC)/ext/armips/Archs/ARM/Pool.cpp \ - $(SRC)/ext/armips/Archs/ARM/ThumbOpcodes.cpp \ - $(SRC)/ext/armips/Archs/MIPS/CMipsInstruction.cpp \ - $(SRC)/ext/armips/Archs/MIPS/Mips.cpp \ - $(SRC)/ext/armips/Archs/MIPS/MipsElfFile.cpp \ - $(SRC)/ext/armips/Archs/MIPS/MipsMacros.cpp \ - $(SRC)/ext/armips/Archs/MIPS/MipsOpcodes.cpp \ - $(SRC)/ext/armips/Archs/MIPS/MipsParser.cpp \ - $(SRC)/ext/armips/Archs/MIPS/PsxRelocator.cpp \ - $(SRC)/ext/armips/Archs/Architecture.cpp \ - $(SRC)/ext/armips/Commands/CAssemblerCommand.cpp \ - $(SRC)/ext/armips/Commands/CAssemblerLabel.cpp \ - $(SRC)/ext/armips/Commands/CDirectiveArea.cpp \ - $(SRC)/ext/armips/Commands/CDirectiveConditional.cpp \ - $(SRC)/ext/armips/Commands/CDirectiveData.cpp \ - $(SRC)/ext/armips/Commands/CDirectiveFile.cpp \ - $(SRC)/ext/armips/Commands/CDirectiveMessage.cpp \ - $(SRC)/ext/armips/Commands/CommandSequence.cpp \ - $(SRC)/ext/armips/Core/ELF/ElfFile.cpp \ - $(SRC)/ext/armips/Core/ELF/ElfRelocator.cpp \ - $(SRC)/ext/armips/Core/Assembler.cpp \ - $(SRC)/ext/armips/Core/Common.cpp \ - $(SRC)/ext/armips/Core/Expression.cpp \ - $(SRC)/ext/armips/Core/ExpressionFunctions.cpp \ - $(SRC)/ext/armips/Core/FileManager.cpp \ - $(SRC)/ext/armips/Core/Misc.cpp \ - $(SRC)/ext/armips/Core/SymbolData.cpp \ - $(SRC)/ext/armips/Core/SymbolTable.cpp \ - $(SRC)/ext/armips/Parser/DirectivesParser.cpp \ - $(SRC)/ext/armips/Parser/ExpressionParser.cpp \ - $(SRC)/ext/armips/Parser/Parser.cpp \ - $(SRC)/ext/armips/Parser/Tokenizer.cpp \ - $(SRC)/ext/armips/Util/ByteArray.cpp \ - $(SRC)/ext/armips/Util/CRC.cpp \ - $(SRC)/ext/armips/Util/EncodingTable.cpp \ - $(SRC)/ext/armips/Util/FileClasses.cpp \ - $(SRC)/ext/armips/Util/Util.cpp - ifeq ($(findstring arm64-v8a,$(TARGET_ARCH_ABI)),arm64-v8a) TESTARMEMITTER_FILE = $(SRC)/unittest/TestArm64Emitter.cpp else ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a) @@ -539,8 +545,6 @@ ifeq ($(UNITTEST),1) LOCAL_MODULE := ppsspp_unittest LOCAL_SRC_FILES := \ - $(LIBARMIPS_FILES) \ - $(SRC)/Core/MIPS/MIPSAsm.cpp \ $(SRC)/unittest/JitHarness.cpp \ $(SRC)/unittest/TestVertexJit.cpp \ $(TESTARMEMITTER_FILE) \ diff --git a/ext/native/util/text/utf8.cpp b/ext/native/util/text/utf8.cpp index 33e780d663..4183463d46 100644 --- a/ext/native/util/text/utf8.cpp +++ b/ext/native/util/text/utf8.cpp @@ -463,6 +463,20 @@ std::wstring ConvertUTF8ToWString(const std::string &source) { #else +std::string ConvertWStringToUTF8(const std::wstring &wstr) { + std::string s; + // Worst case. + s.resize(wstr.size() * 4); + + size_t pos = 0; + for (wchar_t c : wstr) { + pos += UTF8::encode(&s[pos], c); + } + + s.resize(pos); + return s; +} + static size_t ConvertUTF8ToWStringInternal(wchar_t *dest, size_t destSize, const std::string &source) { const wchar_t *const orig = dest; const wchar_t *const destEnd = dest + destSize; diff --git a/ext/native/util/text/utf8.h b/ext/native/util/text/utf8.h index 70dbf80b07..6303c25b45 100644 --- a/ext/native/util/text/utf8.h +++ b/ext/native/util/text/utf8.h @@ -83,6 +83,10 @@ bool UTF8StringHasNonASCII(const char *utf8string); std::string ConvertWStringToUTF8(const std::wstring &wstr); std::string ConvertWStringToUTF8(const wchar_t *wstr); +#else + +std::string ConvertWStringToUTF8(const std::wstring &wstr); + #endif // Dest size in units, not bytes. From dfef902cdbfbc83028c247d6d605778ad5e95e11 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 13 Apr 2018 23:48:04 -0700 Subject: [PATCH 130/301] json: Escape strings in the json writer. --- ext/native/json/json_writer.cpp | 95 ++++++++++++++++++++++++++++----- ext/native/json/json_writer.h | 19 ++++++- 2 files changed, 99 insertions(+), 15 deletions(-) diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index ec9019c50d..ec43a4fae8 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,6 +1,9 @@ +#include #include "json/json_writer.h" -JsonWriter::JsonWriter() { +JsonWriter::JsonWriter(int flags) { + pretty_ = (flags & PRETTY) != 0; + str_.imbue(std::locale::classic()); } JsonWriter::~JsonWriter() { @@ -13,15 +16,24 @@ void JsonWriter::begin() { void JsonWriter::end() { pop(); - str_ << "\n"; + if (pretty_) + str_ << "\n"; } const char *JsonWriter::indent(int n) const { - static const char * const whitespace = " "; + if (!pretty_) + return ""; + static const char * const whitespace = " "; + if (n > 32) { + // Avoid crash. + return whitespace; + } return whitespace + (32 - n); } const char *JsonWriter::indent() const { + if (!pretty_) + return ""; int amount = (int)stack_.size() + 1; amount *= 2; // 2-space indent. return indent(amount); @@ -37,7 +49,7 @@ const char *JsonWriter::comma() const { if (stack_.back().first) { return ""; } else { - return ","; + return ",\n"; } } @@ -45,17 +57,21 @@ const char *JsonWriter::arrayComma() const { if (stack_.back().first) { return "\n"; } else { - return ", "; + return pretty_ ? ", " : ","; } } void JsonWriter::pushDict(const char *name) { - str_ << comma() << "\n" << indent() << "\"" << name << "\": {"; + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << "\": {"; stack_.push_back(StackEntry(DICT)); } void JsonWriter::pushArray(const char *name) { - str_ << comma() << "\n" << indent() << "\"" << name << "\": ["; + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << "\": ["; stack_.push_back(StackEntry(ARRAY)); } @@ -65,7 +81,9 @@ void JsonWriter::writeBool(bool value) { } void JsonWriter::writeBool(const char *name, bool value) { - str_ << comma() << "\n" << indent() << "\"" << name << "\": " << (value ? "true" : "false"); + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << "\": " << (value ? "true" : "false"); stack_.back().first = false; } @@ -75,7 +93,9 @@ void JsonWriter::writeInt(int value) { } void JsonWriter::writeInt(const char *name, int value) { - str_ << comma() << "\n" << indent() << "\"" << name << "\": " << value; + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << "\": " << value; stack_.back().first = false; } @@ -85,31 +105,78 @@ void JsonWriter::writeFloat(double value) { } void JsonWriter::writeFloat(const char *name, double value) { - str_ << comma() << "\n" << indent() << "\"" << name << "\": " << value; + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << "\": " << value; stack_.back().first = false; } void JsonWriter::writeString(const char *value) { - str_ << arrayComma() << arrayIndent() << "\"" << value << "\""; + str_ << arrayComma() << arrayIndent() << "\""; + writeEscapedString(value); + str_ << "\""; stack_.back().first = false; } void JsonWriter::writeString(const char *name, const char *value) { - str_ << comma() << "\n" << indent() << "\"" << name << "\": \"" << value << "\""; + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << "\": \""; + writeEscapedString(value); + str_ << "\""; stack_.back().first = false; } void JsonWriter::pop() { BlockType type = stack_.back().type; stack_.pop_back(); + if (pretty_) + str_ << "\n" << indent(); switch (type) { case ARRAY: - str_ << "\n" << indent() << "]"; + str_ << "]"; break; case DICT: - str_ << "\n" << indent() << "}"; + str_ << "}"; break; } if (stack_.size() > 0) stack_.back().first = false; } + +void JsonWriter::writeEscapedString(const char *str) { + size_t pos = 0; + size_t len = strlen(str); + + auto update = [&](size_t current, size_t skip = 0) { + size_t end = current; + if (pos < end) + str_ << std::string(str + pos, end - pos); + pos = end + skip; + }; + + for (size_t i = 0; i < len; ++i) { + if (str[i] == '\\' || str[i] == '"' || str[i] == '/') { + update(i); + str_ << '\\'; + } else if (str[i] == '\r') { + update(i, 1); + str_ << "\\r"; + } else if (str[i] == '\n') { + update(i, 1); + str_ << "\\n"; + } else if (str[i] == '\t') { + update(i, 1); + str_ << "\\t"; + } else if (str[i] < 32) { + update(i, 1); + str_ << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int)str[i] << std::dec << std::setw(0); + } + } + + if (pos != 0) { + update(len); + } else { + str_ << str; + } +} diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index b58ca37f6d..378a09f5b9 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -16,7 +16,7 @@ class JsonWriter { public: - JsonWriter(); + JsonWriter(int flags = NORMAL); ~JsonWriter(); void begin(); void end(); @@ -31,17 +31,33 @@ public: void writeFloat(const char *name, double value); void writeString(const char *value); void writeString(const char *name, const char *value); + void writeString(const std::string &value) { + writeString(value.c_str()); + } + void writeString(const char *name, const std::string &value) { + writeString(name, value.c_str()); + } + void writeString(const std::string &name, const std::string &value) { + writeString(name.c_str(), value.c_str()); + } std::string str() const { return str_.str(); } + enum { + NORMAL = 0, + PRETTY = 1, + }; + private: const char *indent(int n) const; const char *comma() const; const char *arrayComma() const; const char *indent() const; const char *arrayIndent() const; + void writeEscapedString(const char *s); + enum BlockType { ARRAY, DICT, @@ -53,4 +69,5 @@ private: }; std::vector stack_; std::ostringstream str_; + bool pretty_; }; From fb876e78c0ef8f701302364a19e4b5bb12efcb41 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 10:33:11 -0700 Subject: [PATCH 131/301] json: Actually include in build. --- CMakeLists.txt | 6 ++---- ext/native/json/json_writer.cpp | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e26ebe2227..bc52b1b7a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -919,10 +919,6 @@ add_library(native STATIC ext/native/base/timeutil.h ext/native/data/compression.cpp ext/native/data/compression.h - ext/native/ext/vjson/json.cpp - ext/native/ext/vjson/json.h - ext/native/ext/vjson/block_allocator.cpp - ext/native/ext/vjson/block_allocator.h ext/native/file/chunk_file.cpp ext/native/file/chunk_file.h ext/native/file/fd_util.cpp @@ -976,6 +972,8 @@ add_library(native STATIC ext/native/input/keycodes.h ext/native/input/input_state.h ext/native/input/input_state.cpp + ext/native/json/json_writer.h + ext/native/json/json_writer.cpp ext/native/math/fast/fast_math.c ext/native/math/fast/fast_matrix.c ext/native/math/fast/fast_matrix_neon.S diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index ec43a4fae8..ea826a312c 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,4 +1,5 @@ #include +#include #include "json/json_writer.h" JsonWriter::JsonWriter(int flags) { @@ -49,7 +50,7 @@ const char *JsonWriter::comma() const { if (stack_.back().first) { return ""; } else { - return ",\n"; + return pretty_ ? ",\n" : ","; } } From 03cc4eab0170befe52f220f0efe4f1c9b097c2d5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 14:19:24 -0700 Subject: [PATCH 132/301] json: Allow formatting read json back to writer. --- ext/native/json/json_writer.cpp | 134 ++++++++++++++++++++++++++++++++ ext/native/json/json_writer.h | 20 ++++- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index ea826a312c..95c2a3983f 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,5 +1,6 @@ #include #include +#include "ext/vjson/json.h" #include "json/json_writer.h" JsonWriter::JsonWriter(int flags) { @@ -15,6 +16,16 @@ void JsonWriter::begin() { stack_.push_back(StackEntry(DICT)); } +void JsonWriter::beginArray() { + str_ << "["; + stack_.push_back(StackEntry(ARRAY)); +} + +void JsonWriter::beginRaw() { + // For the uncommon case of writing a value directly, to avoid duplicated code. + stack_.push_back(StackEntry(RAW)); +} + void JsonWriter::end() { pop(); if (pretty_) @@ -41,6 +52,8 @@ const char *JsonWriter::indent() const { } const char *JsonWriter::arrayIndent() const { + if (!pretty_) + return ""; int amount = (int)stack_.size() + 1; amount *= 2; // 2-space indent. return stack_.back().first ? indent(amount) : ""; @@ -128,6 +141,19 @@ void JsonWriter::writeString(const char *name, const char *value) { stack_.back().first = false; } +void JsonWriter::writeRaw(const char *value) { + str_ << arrayComma() << arrayIndent() << value; + stack_.back().first = false; +} + +void JsonWriter::writeRaw(const char *name, const char *value) { + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << (pretty_ ? "\": " : "\":"); + str_ << value; + stack_.back().first = false; +} + void JsonWriter::pop() { BlockType type = stack_.back().type; stack_.pop_back(); @@ -140,6 +166,8 @@ void JsonWriter::pop() { case DICT: str_ << "}"; break; + case RAW: + break; } if (stack_.size() > 0) stack_.back().first = false; @@ -181,3 +209,109 @@ void JsonWriter::writeEscapedString(const char *str) { str_ << str; } } + +static void json_stringify_object(JsonWriter &writer, const json_value *value); +static void json_stringify_array(JsonWriter &writer, const json_value *value); + +std::string json_stringify(const json_value *value) { + JsonWriter writer; + + // Handle direct values too, not just objects. + switch (value->type) { + case JSON_NULL: + case JSON_STRING: + case JSON_INT: + case JSON_FLOAT: + case JSON_BOOL: + writer.beginRaw(); + // It's the same as a one entry array without brackets, so reuse. + json_stringify_array(writer, value); + break; + + case JSON_OBJECT: + writer.begin(); + for (const json_value *it = value->first_child; it; it = it->next_sibling) { + json_stringify_object(writer, it); + } + break; + case JSON_ARRAY: + writer.beginArray(); + for (const json_value *it = value->first_child; it; it = it->next_sibling) { + json_stringify_array(writer, it); + } + break; + } + + writer.end(); + return writer.str(); +} + +static void json_stringify_object(JsonWriter &writer, const json_value *value) { + switch (value->type) { + case JSON_NULL: + writer.writeRaw(value->name, "null"); + break; + case JSON_STRING: + writer.writeString(value->name, value->string_value); + break; + case JSON_INT: + writer.writeInt(value->name, value->int_value); + break; + case JSON_FLOAT: + writer.writeFloat(value->name, value->float_value); + break; + case JSON_BOOL: + writer.writeBool(value->name, value->int_value != 0); + break; + + case JSON_OBJECT: + writer.pushDict(value->name); + for (const json_value *it = value->first_child; it; it = it->next_sibling) { + json_stringify_object(writer, it); + } + writer.pop(); + break; + case JSON_ARRAY: + writer.pushArray(value->name); + for (const json_value *it = value->first_child; it; it = it->next_sibling) { + json_stringify_array(writer, it); + } + writer.pop(); + break; + } +} + +static void json_stringify_array(JsonWriter &writer, const json_value *value) { + switch (value->type) { + case JSON_NULL: + writer.writeRaw("null"); + break; + case JSON_STRING: + writer.writeString(value->string_value); + break; + case JSON_INT: + writer.writeInt(value->int_value); + break; + case JSON_FLOAT: + writer.writeFloat(value->float_value); + break; + case JSON_BOOL: + writer.writeBool(value->int_value != 0); + break; + + case JSON_OBJECT: + writer.begin(); + for (const json_value *it = value->first_child; it; it = it->next_sibling) { + json_stringify_object(writer, it); + } + writer.pop(); + break; + case JSON_ARRAY: + writer.beginArray(); + for (const json_value *it = value->first_child; it; it = it->next_sibling) { + json_stringify_array(writer, it); + } + writer.pop(); + break; + } +} diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index 378a09f5b9..cf4ba87259 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -7,8 +7,7 @@ // // Does not deal with encodings in any way. // -// Zero dependencies apart from stdlib. -// See json_writer_test.cpp for usage. +// Zero dependencies apart from stdlib (if you remove the vhjson usage.) #include #include @@ -19,6 +18,8 @@ public: JsonWriter(int flags = NORMAL); ~JsonWriter(); void begin(); + void beginArray(); + void beginRaw(); void end(); void pushDict(const char *name); void pushArray(const char *name); @@ -40,6 +41,17 @@ public: void writeString(const std::string &name, const std::string &value) { writeString(name.c_str(), value.c_str()); } + void writeRaw(const char *value); + void writeRaw(const char *name, const char *value); + void writeRaw(const std::string &value) { + writeRaw(value.c_str()); + } + void writeRaw(const char *name, const std::string &value) { + writeRaw(name, value.c_str()); + } + void writeRaw(const std::string &name, const std::string &value) { + writeRaw(name.c_str(), value.c_str()); + } std::string str() const { return str_.str(); @@ -61,6 +73,7 @@ private: enum BlockType { ARRAY, DICT, + RAW, }; struct StackEntry { StackEntry(BlockType t) : type(t), first(true) {} @@ -71,3 +84,6 @@ private: std::ostringstream str_; bool pretty_; }; + +struct json_value; +std::string json_stringify(const json_value *json); From b658130924437b1d0f899487509c7dca2a915511 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 20:01:39 -0700 Subject: [PATCH 133/301] json: Fix formatting of nested arrays/objects. --- ext/native/json/json_writer.cpp | 17 +++++++++++++++-- ext/native/json/json_writer.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 95c2a3983f..7e628ab0ec 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -75,13 +75,26 @@ const char *JsonWriter::arrayComma() const { } } +void JsonWriter::pushDict() { + str_ << arrayComma() << arrayIndent() << "{"; + stack_.back().first = false; + stack_.push_back(StackEntry(DICT)); +} + void JsonWriter::pushDict(const char *name) { str_ << comma() << indent() << "\""; writeEscapedString(name); str_ << "\": {"; + stack_.back().first = false; stack_.push_back(StackEntry(DICT)); } +void JsonWriter::pushArray() { + str_ << arrayComma() << arrayIndent() << "["; + stack_.back().first = false; + stack_.push_back(StackEntry(ARRAY)); +} + void JsonWriter::pushArray(const char *name) { str_ << comma() << indent() << "\""; writeEscapedString(name); @@ -300,14 +313,14 @@ static void json_stringify_array(JsonWriter &writer, const json_value *value) { break; case JSON_OBJECT: - writer.begin(); + writer.pushDict(); for (const json_value *it = value->first_child; it; it = it->next_sibling) { json_stringify_object(writer, it); } writer.pop(); break; case JSON_ARRAY: - writer.beginArray(); + writer.pushArray(); for (const json_value *it = value->first_child; it; it = it->next_sibling) { json_stringify_array(writer, it); } diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index cf4ba87259..b2f4f30742 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -21,7 +21,9 @@ public: void beginArray(); void beginRaw(); void end(); + void pushDict(); void pushDict(const char *name); + void pushArray(); void pushArray(const char *name); void pop(); void writeBool(bool value); From 2e3021da0cc062e8770839718fa63fb7ac8bcf11 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 20:09:56 -0700 Subject: [PATCH 134/301] json: Be precise by default. --- ext/native/json/json_writer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 7e628ab0ec..54cb503aa6 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -6,6 +6,8 @@ JsonWriter::JsonWriter(int flags) { pretty_ = (flags & PRETTY) != 0; str_.imbue(std::locale::classic()); + // Let's maximize precision by default. + str_.precision(53); } JsonWriter::~JsonWriter() { From b56249eec172bd73a250a07f54c8d274dc82ea0f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 20:24:12 -0700 Subject: [PATCH 135/301] json: Avoid encoding inf/nan in JSON. It doesn't support them. The common workaround is to use null. --- ext/native/json/json_writer.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 54cb503aa6..6d4c6905e1 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "ext/vjson/json.h" #include "json/json_writer.h" @@ -129,14 +130,22 @@ void JsonWriter::writeInt(const char *name, int value) { } void JsonWriter::writeFloat(double value) { - str_ << arrayComma() << arrayIndent() << value; + str_ << arrayComma() << arrayIndent(); + if (std::isfinite(value)) + str_ << value; + else + str_ << "null"; stack_.back().first = false; } void JsonWriter::writeFloat(const char *name, double value) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": " << value; + str_ << "\": "; + if (std::isfinite(value)) + str_ << value; + else + str_ << "null"; stack_.back().first = false; } From 082ddf512023ee5b47b5eed5019faece56234241 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 11:24:10 -0700 Subject: [PATCH 136/301] json: Switch to gason instead of vjson. From the same author. Most importantly, reads numbers as doubles rather than as signed ints and floats. This allows us to actually read 32 bit unsigned int parameters. Moved all the native customization to a separate json_reader.cpp. --- CMakeLists.txt | 12 +- Core/Config.cpp | 6 +- UI/RemoteISOScreen.cpp | 19 +- UI/Store.cpp | 39 +- UI/Store.h | 4 +- UWP/NativeUWP/NativeUWP.vcxproj | 8 +- UWP/NativeUWP/NativeUWP.vcxproj.filters | 15 - ext/native/Android.mk | 4 +- ext/native/ext/gason/LICENSE | 20 + ext/native/ext/gason/gason.cpp | 334 ++++++++++++ ext/native/ext/gason/gason.h | 135 +++++ ext/native/ext/vjson/CMakeLists.txt | 10 - ext/native/ext/vjson/block_allocator.cpp | 52 -- ext/native/ext/vjson/block_allocator.h | 35 -- ext/native/ext/vjson/json.cpp | 643 ----------------------- ext/native/ext/vjson/json.h | 113 ---- ext/native/ext/vjson/main.cpp | 118 ----- ext/native/json/json_reader.cpp | 119 +++++ ext/native/json/json_reader.h | 85 +++ ext/native/json/json_writer.cpp | 72 +-- ext/native/json/json_writer.h | 4 +- ext/native/native.vcxproj | 8 +- ext/native/native.vcxproj.filters | 30 +- libretro/Makefile.common | 6 +- 24 files changed, 799 insertions(+), 1092 deletions(-) create mode 100644 ext/native/ext/gason/LICENSE create mode 100644 ext/native/ext/gason/gason.cpp create mode 100644 ext/native/ext/gason/gason.h delete mode 100644 ext/native/ext/vjson/CMakeLists.txt delete mode 100644 ext/native/ext/vjson/block_allocator.cpp delete mode 100644 ext/native/ext/vjson/block_allocator.h delete mode 100644 ext/native/ext/vjson/json.cpp delete mode 100644 ext/native/ext/vjson/json.h delete mode 100644 ext/native/ext/vjson/main.cpp create mode 100644 ext/native/json/json_reader.cpp create mode 100644 ext/native/json/json_reader.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bc52b1b7a1..9d1b5200c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -458,11 +458,9 @@ if(USING_GLES2) find_package(X11) endif() -add_library(vjson STATIC - ext/native/ext/vjson/json.cpp - ext/native/ext/vjson/json.h - ext/native/ext/vjson/block_allocator.cpp - ext/native/ext/vjson/block_allocator.h +add_library(gason STATIC + ext/native/ext/gason/gason.cpp + ext/native/ext/gason/gason.h ) add_library(rg_etc1 STATIC @@ -972,6 +970,8 @@ add_library(native STATIC ext/native/input/keycodes.h ext/native/input/input_state.h ext/native/input/input_state.cpp + ext/native/json/json_reader.h + ext/native/json/json_reader.cpp ext/native/json/json_writer.h ext/native/json/json_writer.cpp ext/native/math/fast/fast_math.c @@ -1060,7 +1060,7 @@ if(ANDROID) SET(ATOMIC_LIB atomic) endif() -target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARY} ${ZLIB_LIBRARY} rg_etc1 vjson udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB}) +target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARY} ${ZLIB_LIBRARY} rg_etc1 gason udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB}) if(TARGET Ext::GLEW) target_link_libraries(native Ext::GLEW) endif() diff --git a/Core/Config.cpp b/Core/Config.cpp index 69ec5d6baa..51b232e0eb 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -22,9 +22,9 @@ #include "base/display.h" #include "base/NativeApp.h" -#include "ext/vjson/json.h" #include "file/ini_file.h" #include "i18n/i18n.h" +#include "json/json_reader.h" #include "gfx_es2/gpu_features.h" #include "net/http_client.h" #include "util/text/parsers.h" @@ -1181,13 +1181,13 @@ void Config::DownloadCompletedCallback(http::Download &download) { } JsonReader reader(data.c_str(), data.size()); - const json_value *root = reader.root(); + const JsonGet root = reader.root(); if (!root) { ERROR_LOG(LOADER, "Failed to parse json"); return; } - std::string version = root->getString("version", ""); + std::string version = root.getString("version", ""); const char *gitVer = PPSSPP_GIT_VERSION; Version installed(gitVer); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 2767e72761..c9d71c6ab8 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -21,9 +21,9 @@ #include #include "base/timeutil.h" -#include "ext/vjson/json.h" #include "file/fd_util.h" #include "i18n/i18n.h" +#include "json/json_reader.h" #include "net/http_client.h" #include "net/http_server.h" #include "net/resolve.h" @@ -252,16 +252,19 @@ static bool FindServer(std::string &resultHost, int &resultPort) { return false; } - const json_value *entries = reader.root(); - if (!entries) { + const JsonValue entries = reader.rootArray(); + if (entries.getTag() != JSON_ARRAY) { return false; } std::vector servers; - const json_value *entry = entries->first_child; - while (entry && !scanCancelled) { - const char *host = entry->getString("ip", ""); - int port = entry->getInt("p", 0); + for (const auto pentry : entries) { + JsonGet entry = pentry->value; + if (scanCancelled) + return false; + + const char *host = entry.getString("ip", ""); + int port = entry.getInt("p", 0); char url[1024] = {}; snprintf(url, sizeof(url), "http://%s:%d", host, port); @@ -270,8 +273,6 @@ static bool FindServer(std::string &resultHost, int &resultPort) { if (TryServer(host, port)) { return true; } - - entry = entry->next_sibling; } // None of the local IPs were reachable. diff --git a/UI/Store.cpp b/UI/Store.cpp index 99c79d2213..da2d2f1a4f 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -18,7 +18,7 @@ #include #include "base/basictypes.h" -#include "ext/vjson/json.h" +#include "json/json_reader.h" #include "i18n/i18n.h" #include "ui/screen.h" @@ -392,33 +392,32 @@ void StoreScreen::update() { void StoreScreen::ParseListing(std::string json) { JsonReader reader(json.c_str(), json.size()); - if (!reader.ok()) { + if (!reader.ok() || !reader.root()) { ELOG("Error parsing JSON from store"); connectionError_ = true; RecreateViews(); return; } - json_value *root = reader.root(); - const json_value *entries = root->getArray("entries"); + const JsonGet root = reader.root(); + const JsonNode *entries = root.getArray("entries"); if (entries) { entries_.clear(); - const json_value *game = entries->first_child; - while (game) { + for (const JsonNode *pgame : entries->value) { + JsonGet game = pgame->value; StoreEntry e; e.type = ENTRY_PBPZIP; e.name = GetTranslatedString(game, "name"); e.description = GetTranslatedString(game, "description", ""); - e.author = game->getString("author", "?"); - e.size = game->getInt("size"); - e.downloadURL = game->getString("download-url", ""); - e.iconURL = game->getString("icon-url", ""); - e.hidden = game->getBool("hidden", false); - const char *file = game->getString("file", 0); + e.author = game.getString("author", "?"); + e.size = game.getInt("size"); + e.downloadURL = game.getString("download-url", ""); + e.iconURL = game.getString("icon-url", ""); + e.hidden = game.getBool("hidden", false); + const char *file = game.getString("file", nullptr); if (!file) continue; e.file = file; entries_.push_back(e); - game = game->next_sibling; } } } @@ -541,16 +540,16 @@ std::string StoreScreen::GetStoreJsonURL(std::string storePath) const { return path; } -std::string StoreScreen::GetTranslatedString(const json_value *json, std::string key, const char *fallback) const { - const json_value *dict = json->getDict("en_US"); - if (dict && json->hasChild(lang_.c_str(), JSON_OBJECT)) { - if (json->getDict(lang_.c_str())->hasChild(key.c_str(), JSON_STRING)) { - dict = json->getDict(lang_.c_str()); +std::string StoreScreen::GetTranslatedString(const JsonGet json, std::string key, const char *fallback) const { + JsonGet dict = json.getDict("en_US"); + if (dict && json.hasChild(lang_.c_str(), JSON_OBJECT)) { + if (json.getDict(lang_.c_str()).hasChild(key.c_str(), JSON_STRING)) { + dict = json.getDict(lang_.c_str()); } } - const char *str = 0; + const char *str = nullptr; if (dict) { - str = dict->getString(key.c_str(), 0); + str = dict.getString(key.c_str(), nullptr); } if (str) { return std::string(str); diff --git a/UI/Store.h b/UI/Store.h index 1fff85a4c9..e7786c7c9c 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -29,7 +29,7 @@ // set game specific settings, etc. // Uses GameInfoCache heavily to implement the functionality. -struct json_value; +struct JsonGet; class ProductItemView; enum EntryType { @@ -79,7 +79,7 @@ private: std::vector FilterEntries(); std::string GetStoreJsonURL(std::string storePath) const; - std::string GetTranslatedString(const json_value *json, std::string key, const char *fallback = 0) const; + std::string GetTranslatedString(const JsonGet json, std::string key, const char *fallback = nullptr) const; std::shared_ptr listing_; std::shared_ptr image_; diff --git a/UWP/NativeUWP/NativeUWP.vcxproj b/UWP/NativeUWP/NativeUWP.vcxproj index e6e8d6a89f..da5942bbfb 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj +++ b/UWP/NativeUWP/NativeUWP.vcxproj @@ -328,8 +328,6 @@ - - @@ -338,6 +336,8 @@ + + @@ -1160,8 +1160,6 @@ NotUsing NotUsing - - @@ -1169,6 +1167,8 @@ + + diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index 6b58b4d5c1..ce9603f332 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -37,9 +37,6 @@ {2be24387-0b6a-4253-97fa-b8b6f75a8a4c} - - {7fdd3320-a8e0-42ed-b08b-2d86ba2ff414} - {1a486fc4-bac0-4b33-9139-262272690c74} @@ -214,12 +211,6 @@ i18n - - ext\vjson - - - ext\vjson - ext\libzip @@ -656,12 +647,6 @@ i18n - - ext\vjson - - - ext\vjson - ext\libzip diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 78a75db9d4..bcef6cec25 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -35,8 +35,7 @@ LOCAL_SRC_FILES :=\ ext/jpge/jpgd.cpp \ ext/jpge/jpge.cpp \ ext/sha1/sha1.cpp \ - ext/vjson/json.cpp \ - ext/vjson/block_allocator.cpp \ + ext/gason/gason.cpp \ file/fd_util.cpp \ file/chunk_file.cpp \ file/file_util.cpp \ @@ -44,6 +43,7 @@ LOCAL_SRC_FILES :=\ file/path.cpp \ file/ini_file.cpp \ file/zip_read.cpp \ + json/json_reader.cpp \ json/json_writer.cpp \ i18n/i18n.cpp \ input/gesture_detector.cpp \ diff --git a/ext/native/ext/gason/LICENSE b/ext/native/ext/gason/LICENSE new file mode 100644 index 0000000000..0c4133a47e --- /dev/null +++ b/ext/native/ext/gason/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2015 Ivan Vashchaev + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ext/native/ext/gason/gason.cpp b/ext/native/ext/gason/gason.cpp new file mode 100644 index 0000000000..c3ce4efec7 --- /dev/null +++ b/ext/native/ext/gason/gason.cpp @@ -0,0 +1,334 @@ +#include "gason.h" +#include + +#define JSON_ZONE_SIZE 4096 +#define JSON_STACK_SIZE 32 + +const char *jsonStrError(int err) { + switch (err) { +#define XX(no, str) \ + case JSON_##no: \ + return str; + JSON_ERRNO_MAP(XX) +#undef XX + default: + return "unknown"; + } +} + +void *JsonAllocator::allocate(size_t size) { + size = (size + 7) & ~7; + + if (head && head->used + size <= JSON_ZONE_SIZE) { + char *p = (char *)head + head->used; + head->used += size; + return p; + } + + size_t allocSize = sizeof(Zone) + size; + Zone *zone = (Zone *)malloc(allocSize <= JSON_ZONE_SIZE ? JSON_ZONE_SIZE : allocSize); + if (zone == nullptr) + return nullptr; + zone->used = allocSize; + if (allocSize <= JSON_ZONE_SIZE || head == nullptr) { + zone->next = head; + head = zone; + } else { + zone->next = head->next; + head->next = zone; + } + return (char *)zone + sizeof(Zone); +} + +void JsonAllocator::deallocate() { + while (head) { + Zone *next = head->next; + free(head); + head = next; + } +} + +static inline bool isspace(char c) { + return c == ' ' || (c >= '\t' && c <= '\r'); +} + +static inline bool isdelim(char c) { + return c == ',' || c == ':' || c == ']' || c == '}' || isspace(c) || !c; +} + +static inline bool isdigit(char c) { + return c >= '0' && c <= '9'; +} + +static inline bool isxdigit(char c) { + return (c >= '0' && c <= '9') || ((c & ~' ') >= 'A' && (c & ~' ') <= 'F'); +} + +static inline int char2int(char c) { + if (c <= '9') + return c - '0'; + return (c & ~' ') - 'A' + 10; +} + +static double string2double(char *s, char **endptr) { + char ch = *s; + if (ch == '-') + ++s; + + double result = 0; + while (isdigit(*s)) + result = (result * 10) + (*s++ - '0'); + + if (*s == '.') { + ++s; + + double fraction = 1; + while (isdigit(*s)) { + fraction *= 0.1; + result += (*s++ - '0') * fraction; + } + } + + if (*s == 'e' || *s == 'E') { + ++s; + + double base = 10; + if (*s == '+') + ++s; + else if (*s == '-') { + ++s; + base = 0.1; + } + + unsigned int exponent = 0; + while (isdigit(*s)) + exponent = (exponent * 10) + (*s++ - '0'); + + double power = 1; + for (; exponent; exponent >>= 1, base *= base) + if (exponent & 1) + power *= base; + + result *= power; + } + + *endptr = s; + return ch == '-' ? -result : result; +} + +static inline JsonNode *insertAfter(JsonNode *tail, JsonNode *node) { + if (!tail) + return node->next = node; + node->next = tail->next; + tail->next = node; + return node; +} + +static inline JsonValue listToValue(JsonTag tag, JsonNode *tail) { + if (tail) { + auto head = tail->next; + tail->next = nullptr; + return JsonValue(tag, head); + } + return JsonValue(tag, nullptr); +} + +int jsonParse(char *s, char **endptr, JsonValue *value, JsonAllocator &allocator) { + JsonNode *tails[JSON_STACK_SIZE]; + JsonTag tags[JSON_STACK_SIZE]; + char *keys[JSON_STACK_SIZE]; + JsonValue o; + int pos = -1; + bool separator = true; + JsonNode *node; + *endptr = s; + + while (*s) { + while (isspace(*s)) { + ++s; + if (!*s) break; + } + *endptr = s++; + switch (**endptr) { + case '-': + if (!isdigit(*s) && *s != '.') { + *endptr = s; + return JSON_BAD_NUMBER; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + o = JsonValue(string2double(*endptr, &s)); + if (!isdelim(*s)) { + *endptr = s; + return JSON_BAD_NUMBER; + } + break; + case '"': + o = JsonValue(JSON_STRING, s); + for (char *it = s; *s; ++it, ++s) { + int c = *it = *s; + if (c == '\\') { + c = *++s; + switch (c) { + case '\\': + case '"': + case '/': + *it = c; + break; + case 'b': + *it = '\b'; + break; + case 'f': + *it = '\f'; + break; + case 'n': + *it = '\n'; + break; + case 'r': + *it = '\r'; + break; + case 't': + *it = '\t'; + break; + case 'u': + c = 0; + for (int i = 0; i < 4; ++i) { + if (isxdigit(*++s)) { + c = c * 16 + char2int(*s); + } else { + *endptr = s; + return JSON_BAD_STRING; + } + } + if (c < 0x80) { + *it = c; + } else if (c < 0x800) { + *it++ = 0xC0 | (c >> 6); + *it = 0x80 | (c & 0x3F); + } else { + *it++ = 0xE0 | (c >> 12); + *it++ = 0x80 | ((c >> 6) & 0x3F); + *it = 0x80 | (c & 0x3F); + } + break; + default: + *endptr = s; + return JSON_BAD_STRING; + } + } else if ((unsigned int)c < ' ' || c == '\x7F') { + *endptr = s; + return JSON_BAD_STRING; + } else if (c == '"') { + *it = 0; + ++s; + break; + } + } + if (!isdelim(*s)) { + *endptr = s; + return JSON_BAD_STRING; + } + break; + case 't': + if (!(s[0] == 'r' && s[1] == 'u' && s[2] == 'e' && isdelim(s[3]))) + return JSON_BAD_IDENTIFIER; + o = JsonValue(JSON_TRUE); + s += 3; + break; + case 'f': + if (!(s[0] == 'a' && s[1] == 'l' && s[2] == 's' && s[3] == 'e' && isdelim(s[4]))) + return JSON_BAD_IDENTIFIER; + o = JsonValue(JSON_FALSE); + s += 4; + break; + case 'n': + if (!(s[0] == 'u' && s[1] == 'l' && s[2] == 'l' && isdelim(s[3]))) + return JSON_BAD_IDENTIFIER; + o = JsonValue(JSON_NULL); + s += 3; + break; + case ']': + if (pos == -1) + return JSON_STACK_UNDERFLOW; + if (tags[pos] != JSON_ARRAY) + return JSON_MISMATCH_BRACKET; + o = listToValue(JSON_ARRAY, tails[pos--]); + break; + case '}': + if (pos == -1) + return JSON_STACK_UNDERFLOW; + if (tags[pos] != JSON_OBJECT) + return JSON_MISMATCH_BRACKET; + if (keys[pos] != nullptr) + return JSON_UNEXPECTED_CHARACTER; + o = listToValue(JSON_OBJECT, tails[pos--]); + break; + case '[': + if (++pos == JSON_STACK_SIZE) + return JSON_STACK_OVERFLOW; + tails[pos] = nullptr; + tags[pos] = JSON_ARRAY; + keys[pos] = nullptr; + separator = true; + continue; + case '{': + if (++pos == JSON_STACK_SIZE) + return JSON_STACK_OVERFLOW; + tails[pos] = nullptr; + tags[pos] = JSON_OBJECT; + keys[pos] = nullptr; + separator = true; + continue; + case ':': + if (separator || keys[pos] == nullptr) + return JSON_UNEXPECTED_CHARACTER; + separator = true; + continue; + case ',': + if (separator || keys[pos] != nullptr) + return JSON_UNEXPECTED_CHARACTER; + separator = true; + continue; + case '\0': + continue; + default: + return JSON_UNEXPECTED_CHARACTER; + } + + separator = false; + + if (pos == -1) { + *endptr = s; + *value = o; + return JSON_OK; + } + + if (tags[pos] == JSON_OBJECT) { + if (!keys[pos]) { + if (o.getTag() != JSON_STRING) + return JSON_UNQUOTED_KEY; + keys[pos] = o.toString(); + continue; + } + if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode))) == nullptr) + return JSON_ALLOCATION_FAILURE; + tails[pos] = insertAfter(tails[pos], node); + tails[pos]->key = keys[pos]; + keys[pos] = nullptr; + } else { + if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode) - sizeof(char *))) == nullptr) + return JSON_ALLOCATION_FAILURE; + tails[pos] = insertAfter(tails[pos], node); + } + tails[pos]->value = o; + } + return JSON_BREAKING_BAD; +} diff --git a/ext/native/ext/gason/gason.h b/ext/native/ext/gason/gason.h new file mode 100644 index 0000000000..7c3eed96f6 --- /dev/null +++ b/ext/native/ext/gason/gason.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include +#include + +enum JsonTag { + JSON_NUMBER = 0, + JSON_STRING, + JSON_ARRAY, + JSON_OBJECT, + JSON_TRUE, + JSON_FALSE, + JSON_NULL = 0xF +}; + +struct JsonNode; + +#define JSON_VALUE_PAYLOAD_MASK 0x00007FFFFFFFFFFFULL +#define JSON_VALUE_NAN_MASK 0x7FF8000000000000ULL +#define JSON_VALUE_TAG_MASK 0xF +#define JSON_VALUE_TAG_SHIFT 47 + +union JsonValue { + uint64_t ival; + double fval; + + JsonValue(double x) + : fval(x) { + } + JsonValue(JsonTag tag = JSON_NULL, void *payload = nullptr) { + assert((uintptr_t)payload <= JSON_VALUE_PAYLOAD_MASK); + ival = JSON_VALUE_NAN_MASK | ((uint64_t)tag << JSON_VALUE_TAG_SHIFT) | (uintptr_t)payload; + } + bool isDouble() const { + return (int64_t)ival <= (int64_t)JSON_VALUE_NAN_MASK; + } + JsonTag getTag() const { + return isDouble() ? JSON_NUMBER : JsonTag((ival >> JSON_VALUE_TAG_SHIFT) & JSON_VALUE_TAG_MASK); + } + uint64_t getPayload() const { + assert(!isDouble()); + return ival & JSON_VALUE_PAYLOAD_MASK; + } + double toNumber() const { + assert(getTag() == JSON_NUMBER); + return fval; + } + char *toString() const { + assert(getTag() == JSON_STRING); + return (char *)getPayload(); + } + JsonNode *toNode() const { + assert(getTag() == JSON_ARRAY || getTag() == JSON_OBJECT); + return (JsonNode *)getPayload(); + } +}; + +struct JsonNode { + JsonValue value; + JsonNode *next; + char *key; +}; + +struct JsonIterator { + JsonNode *p; + + void operator++() { + p = p->next; + } + bool operator!=(const JsonIterator &x) const { + return p != x.p; + } + JsonNode *operator*() const { + return p; + } + JsonNode *operator->() const { + return p; + } +}; + +inline JsonIterator begin(JsonValue o) { + return JsonIterator{o.toNode()}; +} +inline JsonIterator end(JsonValue) { + return JsonIterator{nullptr}; +} + +#define JSON_ERRNO_MAP(XX) \ + XX(OK, "ok") \ + XX(BAD_NUMBER, "bad number") \ + XX(BAD_STRING, "bad string") \ + XX(BAD_IDENTIFIER, "bad identifier") \ + XX(STACK_OVERFLOW, "stack overflow") \ + XX(STACK_UNDERFLOW, "stack underflow") \ + XX(MISMATCH_BRACKET, "mismatch bracket") \ + XX(UNEXPECTED_CHARACTER, "unexpected character") \ + XX(UNQUOTED_KEY, "unquoted key") \ + XX(BREAKING_BAD, "breaking bad") \ + XX(ALLOCATION_FAILURE, "allocation failure") + +enum JsonErrno { +#define XX(no, str) JSON_##no, + JSON_ERRNO_MAP(XX) +#undef XX +}; + +const char *jsonStrError(int err); + +class JsonAllocator { + struct Zone { + Zone *next; + size_t used; + } *head; + +public: + JsonAllocator() : head(nullptr) {}; + JsonAllocator(const JsonAllocator &) = delete; + JsonAllocator &operator=(const JsonAllocator &) = delete; + JsonAllocator(JsonAllocator &&x) : head(x.head) { + x.head = nullptr; + } + JsonAllocator &operator=(JsonAllocator &&x) { + head = x.head; + x.head = nullptr; + return *this; + } + ~JsonAllocator() { + deallocate(); + } + void *allocate(size_t size); + void deallocate(); +}; + +int jsonParse(char *str, char **endptr, JsonValue *value, JsonAllocator &allocator); diff --git a/ext/native/ext/vjson/CMakeLists.txt b/ext/native/ext/vjson/CMakeLists.txt deleted file mode 100644 index b8d19dc34f..0000000000 --- a/ext/native/ext/vjson/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.6) - -#if(UNIX) -add_definitions(-fPIC) -add_definitions(-g) -add_definitions(-O2) -add_definitions(-Wall) -#endif(UNIX) - -add_library(vjson json.cpp block_allocator.cpp) diff --git a/ext/native/ext/vjson/block_allocator.cpp b/ext/native/ext/vjson/block_allocator.cpp deleted file mode 100644 index f3a573c653..0000000000 --- a/ext/native/ext/vjson/block_allocator.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include "block_allocator.h" - -block_allocator::block_allocator(size_t blocksize) - : m_head(0), m_blocksize(blocksize) -{ -} - -block_allocator::~block_allocator() -{ - while (m_head) - { - block *temp = m_head->next; - ::free(m_head); - m_head = temp; - } -} - -void block_allocator::swap(block_allocator &rhs) -{ - std::swap(m_blocksize, rhs.m_blocksize); - std::swap(m_head, rhs.m_head); -} - -void *block_allocator::Malloc(size_t size) -{ - if ((m_head && m_head->used + size > m_head->size) || !m_head) - { - // calc needed size for allocation - size_t alloc_size = std::max(sizeof(block) + size, m_blocksize); - - // create new block - char *buffer = (char *)::malloc(alloc_size); - block *b = reinterpret_cast(buffer); - b->size = alloc_size; - b->used = sizeof(block); - b->buffer = buffer; - b->next = m_head; - m_head = b; - } - - void *ptr = m_head->buffer + m_head->used; - m_head->used += size; - return ptr; -} - -void block_allocator::Free() -{ - block_allocator(0).swap(*this); -} diff --git a/ext/native/ext/vjson/block_allocator.h b/ext/native/ext/vjson/block_allocator.h deleted file mode 100644 index 9fff7efc50..0000000000 --- a/ext/native/ext/vjson/block_allocator.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef BLOCK_ALLOCATOR_H -#define BLOCK_ALLOCATOR_H - -class block_allocator -{ -public: - block_allocator(size_t blocksize); - ~block_allocator(); - - // exchange contents with rhs - void swap(block_allocator &rhs); - - // allocate memory - void *Malloc(size_t size); - - // free all allocated blocks - void Free(); - -private: - struct block - { - size_t size; - size_t used; - char *buffer; - block *next; - }; - - block *m_head; - size_t m_blocksize; - - block_allocator(const block_allocator &); - block_allocator &operator=(block_allocator &); -}; - -#endif diff --git a/ext/native/ext/vjson/json.cpp b/ext/native/ext/vjson/json.cpp deleted file mode 100644 index 7203cb577b..0000000000 --- a/ext/native/ext/vjson/json.cpp +++ /dev/null @@ -1,643 +0,0 @@ -#include -#include -#include -#include "json.h" -#include "file/zip_read.h" -#include "file/vfs.h" - -// true if character represent a digit -#define IS_DIGIT(c) (c >= '0' && c <= '9') - -int json_value::numChildren() const { - int count = 0; - const json_value *c = first_child; - while (c) { - count++; - c = c->next_sibling; - } - return count; -} - -// only works right for first child. includes itself in count. -int json_value::numSiblings() const { - const json_value *s = next_sibling; - int count = 1; - while (s) { - count++; - s = s->next_sibling; - } - return count; -} - -const json_value *json_value::get(const char *child_name) const { - if (!child_name) { - FLOG("JSON: Cannot get from null child name"); - return 0; - } - const json_value *c = first_child; - while (c) { - if (!strcmp(c->name, child_name)) { - return c; - } - c = c->next_sibling; - } - return 0; -} - -const json_value *json_value::get(const char *child_name, json_type type) const { - const json_value *v = get(child_name); - if (v && type == v->type) - return v; - else - return 0; -} - -const char *json_value::getStringOrDie(const char *child_name) const { - const json_value *val = get(child_name, JSON_STRING); - if (val) - return val->string_value; - else - FLOG("String '%s' missing from node '%s'", child_name, this->name); - return 0; -} - -const char *json_value::getString(const char *child_name, const char *default_value) const { - const json_value *val = get(child_name, JSON_STRING); - if (!val) - return default_value; - return val->string_value; -} - -bool json_value::getStringVector(std::vector *vec) const { - vec->clear(); - if (type == JSON_ARRAY) { - json_value *val = first_child; - while (val) { - if (val->type == JSON_STRING) { - vec->push_back(val->string_value); - } - } - return true; - } else { - return false; - } -} - -float json_value::getFloat(const char *child_name) const { - return get(child_name, JSON_FLOAT)->float_value; -} - -float json_value::getFloat(const char *child_name, float default_value) const { - const json_value *val = get(child_name, JSON_FLOAT); - if (!val) { - // Let's try int. - val = get(child_name, JSON_INT); - if (!val) - return default_value; - return val->int_value; - } - return val->float_value; -} - -int json_value::getInt(const char *child_name) const { - return get(child_name, JSON_INT)->int_value; -} - -int json_value::getInt(const char *child_name, int default_value) const { - const json_value *val = get(child_name, JSON_INT); - if (!val) - return default_value; - return val->int_value; -} - -bool json_value::getBool(const char *child_name) const { - return get(child_name, JSON_BOOL)->int_value != 0 ? true : false; -} - -bool json_value::getBool(const char *child_name, bool default_value) const { - const json_value *val = get(child_name, JSON_BOOL); - if (!val) - return default_value; - return val->int_value != 0 ? true : false; -} - - -// convert string to integer -char *atoi(char *first, char *last, int *out) -{ - int sign = 1; - if (first != last) - { - if (*first == '-') - { - sign = -1; - ++first; - } - else if (*first == '+') - { - ++first; - } - } - - int result = 0; - for (; first != last && IS_DIGIT(*first); ++first) - { - result = 10 * result + (*first - '0'); - } - *out = result * sign; - - return first; -} - -// convert hexadecimal string to unsigned integer -char *hatoui(char *first, char *last, unsigned int *out) -{ - unsigned int result = 0; - for (; first != last; ++first) - { - int digit; - if (IS_DIGIT(*first)) - { - digit = *first - '0'; - } - else if (*first >= 'a' && *first <= 'f') - { - digit = *first - 'a' + 10; - } - else if (*first >= 'A' && *first <= 'F') - { - digit = *first - 'A' + 10; - } - else - { - break; - } - result = 16 * result + digit; - } - *out = result; - - return first; -} - -// convert string to floating point -char *atof(char *first, char *last, float *out) -{ - // sign - float sign = 1; - if (first != last) - { - if (*first == '-') - { - sign = -1; - ++first; - } - else if (*first == '+') - { - ++first; - } - } - - // integer part - float result = 0; - for (; first != last && IS_DIGIT(*first); ++first) - { - result = 10 * result + (*first - '0'); - } - - // fraction part - if (first != last && *first == '.') - { - ++first; - - float inv_base = 0.1f; - for (; first != last && IS_DIGIT(*first); ++first) - { - result += (*first - '0') * inv_base; - inv_base *= 0.1f; - } - } - - // result w\o exponent - result *= sign; - - // exponent - bool exponent_negative = false; - int exponent = 0; - if (first != last && (*first == 'e' || *first == 'E')) - { - ++first; - - if (*first == '-') - { - exponent_negative = true; - ++first; - } - else if (*first == '+') - { - ++first; - } - - for (; first != last && IS_DIGIT(*first); ++first) - { - exponent = 10 * exponent + (*first - '0'); - } - } - - if (exponent) - { - float power_of_ten = 10; - for (; exponent > 1; exponent--) - { - power_of_ten *= 10; - } - - if (exponent_negative) - { - result /= power_of_ten; - } - else - { - result *= power_of_ten; - } - } - - *out = result; - - return first; -} - -json_value *json_alloc(block_allocator *allocator) -{ - json_value *value = (json_value *)allocator->Malloc(sizeof(json_value)); - memset(value, 0, sizeof(json_value)); - return value; -} - -void json_append(json_value *lhs, json_value *rhs) -{ - rhs->parent = lhs; - if (lhs->last_child) - { - lhs->last_child = lhs->last_child->next_sibling = rhs; - } - else - { - lhs->first_child = lhs->last_child = rhs; - } -} - -#undef ERROR - -#define ERROR(it, desc)\ - *error_pos = it;\ - *error_desc = (char *)desc;\ - *error_line = 1 - escaped_newlines;\ - for (char *c = it; c != source; --c)\ - if (*c == '\n') ++*error_line;\ - return 0 - -#define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");} - -json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator) -{ - json_value *root = 0; - json_value *top = 0; - - char *name = 0; - char *it = source; - - int escaped_newlines = 0; - - while (*it) - { - switch (*it) - { - case '{': - case '[': - { - // create new value - json_value *object = json_alloc(allocator); - - // name - object->name = name; - name = 0; - - // type - object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY; - - // skip open character - ++it; - - // set top and root - if (top) - { - json_append(top, object); - } - else if (!root) - { - root = object; - } - else - { - ERROR(it, "Second root. Only one root allowed"); - } - top = object; - } - break; - - case '}': - case ']': - { - if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY)) - { - ERROR(it, "Mismatch closing brace/bracket"); - } - - // skip close character - ++it; - - // set top - top = top->parent; - } - break; - - case ':': - if (!top || top->type != JSON_OBJECT) - { - ERROR(it, "Unexpected character (no top level)"); - } - ++it; - break; - - case ',': - CHECK_TOP(); - ++it; - break; - - case '"': - { - CHECK_TOP(); - - // skip '"' character - ++it; - - char *first = it; - char *last = it; - while (*it) - { - if ((unsigned char)*it < '\x20') - { - ERROR(first, "Control characters not allowed in strings"); - } - else if (*it == '\\') - { - switch (it[1]) - { - case '"': - *last = '"'; - break; - case '\\': - *last = '\\'; - break; - case '/': - *last = '/'; - break; - case 'b': - *last = '\b'; - break; - case 'f': - *last = '\f'; - break; - case 'n': - *last = '\n'; - ++escaped_newlines; - break; - case 'r': - *last = '\r'; - break; - case 't': - *last = '\t'; - break; - case 'u': - { - unsigned int codepoint; - if (hatoui(it + 2, it + 6, &codepoint) != it + 6) - { - ERROR(it, "Bad unicode codepoint"); - } - - if (codepoint <= 0x7F) - { - *last = (char)codepoint; - } - else if (codepoint <= 0x7FF) - { - *last++ = (char)(0xC0 | (codepoint >> 6)); - *last = (char)(0x80 | (codepoint & 0x3F)); - } - else if (codepoint <= 0xFFFF) - { - *last++ = (char)(0xE0 | (codepoint >> 12)); - *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F)); - *last = (char)(0x80 | (codepoint & 0x3F)); - } - } - it += 4; - break; - default: - ERROR(first, "Unrecognized escape sequence"); - } - - ++last; - it += 2; - } - else if (*it == '"') - { - *last = 0; - ++it; - break; - } - else - { - *last++ = *it++; - } - } - - if (!name && top->type == JSON_OBJECT) - { - // field name in object - name = first; - } - else - { - // new string value - json_value *object = json_alloc(allocator); - - object->name = name; - name = 0; - - object->type = JSON_STRING; - object->string_value = first; - - json_append(top, object); - } - } - break; - - case 'n': - case 't': - case 'f': - { - CHECK_TOP(); - - // new null/bool value - json_value *object = json_alloc(allocator); - - object->name = name; - name = 0; - - // null - if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l') - { - object->type = JSON_NULL; - it += 4; - } - // true - else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e') - { - object->type = JSON_BOOL; - object->int_value = 1; - it += 4; - } - // false - else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e') - { - object->type = JSON_BOOL; - object->int_value = 0; - it += 5; - } - else - { - ERROR(it, "Unknown identifier"); - } - - json_append(top, object); - } - break; - - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - CHECK_TOP(); - - // new number value - json_value *object = json_alloc(allocator); - - object->name = name; - name = 0; - - object->type = JSON_INT; - - char *first = it; - while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}') - { - if (*it == '.' || *it == 'e' || *it == 'E') - { - object->type = JSON_FLOAT; - } - ++it; - } - - if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it) - { - ERROR(first, "Bad integer number"); - } - - if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it) - { - ERROR(first, "Bad float number"); - } - - json_append(top, object); - } - break; - - default: - ERROR(it, "Unexpected character (unknown)"); - } - - // skip white space - while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA') - { - ++it; - } - } - - if (top) - { - ERROR(it, "Not all objects/arrays have been properly closed"); - } - - return root; -} - -#define IDENT(n) for (int i = 0; i < n; ++i) printf(" ") - -void json_print(json_value *value, int ident) -{ - IDENT(ident); - if (value->name) printf("\"%s\" = ", value->name); - switch(value->type) - { - case JSON_NULL: - printf("null\n"); - break; - case JSON_OBJECT: - case JSON_ARRAY: - printf(value->type == JSON_OBJECT ? "{\n" : "[\n"); - for (json_value *it = value->first_child; it; it = it->next_sibling) - { - json_print(it, ident + 1); - } - IDENT(ident); - printf(value->type == JSON_OBJECT ? "}\n" : "]\n"); - break; - case JSON_STRING: - printf("\"%s\"\n", value->string_value); - break; - case JSON_INT: - printf("%d\n", value->int_value); - break; - case JSON_FLOAT: - printf("%f\n", value->float_value); - break; - case JSON_BOOL: - printf(value->int_value ? "true\n" : "false\n"); - break; - } -} - -JsonReader::JsonReader(const std::string &filename) : alloc_(1 << 12), root_(0) { - size_t buf_size; - buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size); - if (buffer_) { - parse(); - } else { - // Okay, try to read on the local file system - buffer_ = (char *)ReadLocalFile(filename.c_str(), &buf_size); - if (buffer_) { - parse(); - } else { - ELOG("Failed to read json %s", filename.c_str()); - } - } -} diff --git a/ext/native/ext/vjson/json.h b/ext/native/ext/vjson/json.h deleted file mode 100644 index 792ab1cba4..0000000000 --- a/ext/native/ext/vjson/json.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include -#include -#include -#include "base/logging.h" -#include "block_allocator.h" -#include "file/vfs.h" - -enum json_type { - JSON_NULL, - JSON_OBJECT, - JSON_ARRAY, - JSON_STRING, - JSON_INT, - JSON_FLOAT, - JSON_BOOL, -}; - -struct json_value { - json_value() {} - - json_value *parent; - json_value *next_sibling; - json_value *first_child; - json_value *last_child; - - char *name; - union - { - char *string_value; - int int_value; - float float_value; - }; - - json_type type; - - int numChildren() const; - int numSiblings() const; // num siblings *after* this one only - const json_value *get(const char *child_name) const; - const json_value *get(const char *child_name, json_type type) const; - const json_value *getArray(const char *child_name) const { - return get(child_name, JSON_ARRAY); - } - const json_value *getDict(const char *child_name) const { - return get(child_name, JSON_OBJECT); - } - const char *getStringOrDie(const char *child_name) const; - const char *getString(const char *child_name, const char *default_value) const; - bool getStringVector(std::vector *vec) const; - float getFloat(const char *child_name) const; - float getFloat(const char *child_name, float default_value) const; - int getInt(const char *child_name) const; - int getInt(const char *child_name, int default_value) const; - bool getBool(const char *child_name) const; - bool getBool(const char *child_name, bool default_value) const; - - bool hasChild(const char *child_name, json_type child_type) const { - return get(child_name, child_type) != 0; - } - -private: - DISALLOW_COPY_AND_ASSIGN(json_value); -}; - -// low level interface -json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator); -void json_print(json_value *value, int ident = 0); - -// Easy-wrapper -class JsonReader { -public: - JsonReader(const std::string &filename); - JsonReader(const char *data, size_t size) : alloc_(1 << 12) { - buffer_ = (char *)malloc(size + 1); - memcpy(buffer_, data, size); - buffer_[size] = 0; - parse(); - } - - ~JsonReader() { - if (buffer_) - free(buffer_); - } - - bool ok() const { return root_ != 0; } - - json_value *root() { return root_; } - const json_value *root() const { return root_; } - - void print() { - json_print(root_); - } - -private: - bool parse() { - char *error_pos; - char *error_desc; - int error_line; - root_ = json_parse((char *)buffer_, &error_pos, &error_desc, &error_line, &alloc_); - if (!root_) { - ELOG("Error at (%i): %s\n%s\n\n", error_line, error_desc, error_pos); - return false; - } - return true; - } - - char *buffer_; - block_allocator alloc_; - json_value *root_; - - DISALLOW_COPY_AND_ASSIGN(JsonReader); -}; diff --git a/ext/native/ext/vjson/main.cpp b/ext/native/ext/vjson/main.cpp deleted file mode 100644 index 81f72e5c28..0000000000 --- a/ext/native/ext/vjson/main.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include "json.h" - -void populate_sources(const char *filter, std::vector > &sources) -{ - char filename[256]; - for (int i = 1; i < 64; ++i) - { - sprintf(filename, filter, i); - FILE *fp = fopen(filename, "rb"); - if (fp) - { - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - std::vector buffer(size + 1); - fread (&buffer[0], 1, size, fp); - fclose(fp); - sources.push_back(buffer); - } - else - { - break; - } - } - - printf("Loaded %d json files\n", (int)sources.size()); -} - -#define IDENT(n) for (int i = 0; i < n; ++i) printf(" ") - -void print(json_value *value, int ident = 0) -{ - IDENT(ident); - if (value->name) printf("\"%s\" = ", value->name); - switch(value->type) - { - case JSON_NULL: - printf("null\n"); - break; - case JSON_OBJECT: - case JSON_ARRAY: - printf(value->type == JSON_OBJECT ? "{\n" : "[\n"); - for (json_value *it = value->first_child; it; it = it->next_sibling) - { - print(it, ident + 1); - } - IDENT(ident); - printf(value->type == JSON_OBJECT ? "}\n" : "]\n"); - break; - case JSON_STRING: - printf("\"%s\"\n", value->string_value); - break; - case JSON_INT: - printf("%d\n", value->int_value); - break; - case JSON_FLOAT: - printf("%f\n", value->float_value); - break; - case JSON_BOOL: - printf(value->int_value ? "true\n" : "false\n"); - break; - } -} - -bool parse(char *source) -{ - char *errorPos = 0; - char *errorDesc = 0; - int errorLine = 0; - block_allocator allocator(1 << 10); - - json_value *root = json_parse(source, &errorPos, &errorDesc, &errorLine, &allocator); - if (root) - { - print(root); - return true; - } - - printf("Error at line %d: %s\n%s\n\n", errorLine, errorDesc, errorPos); - return false; -} - -int main(int argc, char **argv) -{ - // Fail - printf("===FAIL===\n\n"); - std::vector > sources; - populate_sources("test/fail%d.json", sources); - int passed = 0; - for (size_t i = 0; i < sources.size(); ++i) - { - printf("Parsing %d\n", (int)i + 1); - if (parse(&sources[i][0])) - { - ++passed; - } - } - printf("Passed %d from %d tests\n", passed, (int)sources.size()); - - // Pass - sources.clear(); - printf("\n===PASS===\n\n"); - populate_sources("test/pass%d.json", sources); - passed = 0; - for (size_t i = 0; i < sources.size(); ++i) - { - printf("Parsing %d\n", (int)i + 1); - if (parse(&sources[i][0])) - { - ++passed; - } - } - printf("Passed %d from %d tests\n", passed, (int)sources.size()); - - return 0; -} diff --git a/ext/native/json/json_reader.cpp b/ext/native/json/json_reader.cpp new file mode 100644 index 0000000000..83fbe94fa4 --- /dev/null +++ b/ext/native/json/json_reader.cpp @@ -0,0 +1,119 @@ +#include "file/zip_read.h" +#include "file/vfs.h" +#include "json/json_reader.h" + +JsonReader::JsonReader(const std::string &filename) { + size_t buf_size; + buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size); + if (buffer_) { + parse(); + } else { + // Okay, try to read on the local file system + buffer_ = (char *)ReadLocalFile(filename.c_str(), &buf_size); + if (buffer_) { + parse(); + } else { + ELOG("Failed to read json %s", filename.c_str()); + } + } +} + +int JsonGet::numChildren() const { + int count = 0; + if (value_.getTag() == JSON_OBJECT || value_.getTag() == JSON_ARRAY) { + for (auto it : value_) { + count++; + } + } + return count; +} + +const JsonNode *JsonGet::get(const char *child_name) const { + if (!child_name) { + FLOG("JSON: Cannot get from null child name"); + return nullptr; + } + if (value_.getTag() != JSON_OBJECT) { + return nullptr; + } + for (auto it : value_) { + if (!strcmp(it->key, child_name)) { + return it; + } + } + return nullptr; +} + +const JsonNode *JsonGet::get(const char *child_name, JsonTag type) const { + const JsonNode *v = get(child_name); + if (v && type == v->value.getTag()) + return v; + return nullptr; +} + +const char *JsonGet::getStringOrDie(const char *child_name) const { + const JsonNode *val = get(child_name, JSON_STRING); + if (val) + return val->value.toString(); + FLOG("String '%s' missing from node", child_name); + return nullptr; +} + +const char *JsonGet::getString(const char *child_name, const char *default_value) const { + const JsonNode *val = get(child_name, JSON_STRING); + if (!val) + return default_value; + return val->value.toString(); +} + +bool JsonGet::getStringVector(std::vector *vec) const { + vec->clear(); + if (value_.getTag() == JSON_ARRAY) { + for (auto it : value_) { + if (it->value.getTag() == JSON_STRING) { + vec->push_back(it->value.toString()); + } + } + return true; + } else { + return false; + } +} + +double JsonGet::getFloat(const char *child_name) const { + return get(child_name, JSON_NUMBER)->value.toNumber(); +} + +double JsonGet::getFloat(const char *child_name, double default_value) const { + const JsonNode *val = get(child_name, JSON_NUMBER); + if (!val) + return default_value; + return val->value.toNumber(); +} + +int JsonGet::getInt(const char *child_name) const { + return (int)get(child_name, JSON_NUMBER)->value.toNumber(); +} + +int JsonGet::getInt(const char *child_name, int default_value) const { + const JsonNode *val = get(child_name, JSON_NUMBER); + if (!val) + return default_value; + return (int)val->value.toNumber(); +} + +bool JsonGet::getBool(const char *child_name) const { + return get(child_name)->value.getTag() == JSON_TRUE; +} + +bool JsonGet::getBool(const char *child_name, bool default_value) const { + const JsonNode *val = get(child_name); + if (val) { + JsonTag tag = val->value.getTag(); + if (tag == JSON_TRUE) + return true; + if (tag == JSON_FALSE) + return false; + } + return default_value; +} diff --git a/ext/native/json/json_reader.h b/ext/native/json/json_reader.h new file mode 100644 index 0000000000..b9e8de5e53 --- /dev/null +++ b/ext/native/json/json_reader.h @@ -0,0 +1,85 @@ +#include +#include +#include "base/basictypes.h" +#include "base/logging.h" +#include "ext/gason/gason.h" + +struct JsonGet { + JsonGet(const JsonValue &value) : value_(value) { + } + + int numChildren() const; + const JsonNode *get(const char *child_name) const; + const JsonNode *get(const char *child_name, JsonTag type) const; + const JsonNode *getArray(const char *child_name) const { + return get(child_name, JSON_ARRAY); + } + const JsonGet getDict(const char *child_name) const { + return JsonGet(get(child_name, JSON_OBJECT)->value); + } + const char *getStringOrDie(const char *child_name) const; + const char *getString(const char *child_name, const char *default_value) const; + bool getStringVector(std::vector *vec) const; + double getFloat(const char *child_name) const; + double getFloat(const char *child_name, double default_value) const; + int getInt(const char *child_name) const; + int getInt(const char *child_name, int default_value) const; + bool getBool(const char *child_name) const; + bool getBool(const char *child_name, bool default_value) const; + + bool hasChild(const char *child_name, JsonTag child_type) const { + return get(child_name, child_type) != nullptr; + } + + operator bool() const { + return value_.getTag() != JSON_NULL; + } + + JsonValue value_; +}; + +// Easy-wrapper +class JsonReader { +public: + JsonReader(const std::string &filename); + JsonReader(const void *data, size_t size) { + buffer_ = (char *)malloc(size + 1); + memcpy(buffer_, data, size); + buffer_[size] = 0; + parse(); + } + JsonReader(const JsonNode *node) { + ok_ = true; + } + + ~JsonReader() { + if (buffer_) + free(buffer_); + } + + bool ok() const { return ok_; } + + JsonGet root() { return root_.getTag() == JSON_OBJECT ? JsonGet(root_) : JsonGet(JSON_NULL); } + const JsonValue rootArray() const { return root_.getTag() == JSON_ARRAY ? root_ : JSON_NULL; } + + const JsonValue rootValue() const { return root_; } + +private: + bool parse() { + char *error_pos; + int status = jsonParse(buffer_, &error_pos, &root_, alloc_); + if (status != JSON_OK) { + ELOG("Error at (%i): %s\n%s\n\n", (int)(error_pos - buffer_), jsonStrError(status), error_pos); + return false; + } + ok_ = true; + return true; + } + + char *buffer_ = nullptr; + JsonAllocator alloc_; + JsonValue root_; + bool ok_ = false; + + DISALLOW_COPY_AND_ASSIGN(JsonReader); +}; diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 6d4c6905e1..3cc625e13d 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "ext/vjson/json.h" +#include "json/json_reader.h" #include "json/json_writer.h" JsonWriter::JsonWriter(int flags) { @@ -234,33 +234,33 @@ void JsonWriter::writeEscapedString(const char *str) { } } -static void json_stringify_object(JsonWriter &writer, const json_value *value); -static void json_stringify_array(JsonWriter &writer, const json_value *value); +static void json_stringify_object(JsonWriter &writer, const JsonNode *node); +static void json_stringify_array(JsonWriter &writer, const JsonNode *node); -std::string json_stringify(const json_value *value) { +std::string json_stringify(const JsonNode *node) { JsonWriter writer; // Handle direct values too, not just objects. - switch (value->type) { + switch (node->value.getTag()) { case JSON_NULL: case JSON_STRING: - case JSON_INT: - case JSON_FLOAT: - case JSON_BOOL: + case JSON_NUMBER: + case JSON_TRUE: + case JSON_FALSE: writer.beginRaw(); // It's the same as a one entry array without brackets, so reuse. - json_stringify_array(writer, value); + json_stringify_array(writer, node); break; case JSON_OBJECT: writer.begin(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_object(writer, it); } break; case JSON_ARRAY: writer.beginArray(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_array(writer, it); } break; @@ -270,34 +270,34 @@ std::string json_stringify(const json_value *value) { return writer.str(); } -static void json_stringify_object(JsonWriter &writer, const json_value *value) { - switch (value->type) { +static void json_stringify_object(JsonWriter &writer, const JsonNode *node) { + switch (node->value.getTag()) { case JSON_NULL: - writer.writeRaw(value->name, "null"); + writer.writeRaw(node->key, "null"); break; case JSON_STRING: - writer.writeString(value->name, value->string_value); + writer.writeString(node->key, node->value.toString()); break; - case JSON_INT: - writer.writeInt(value->name, value->int_value); + case JSON_NUMBER: + writer.writeFloat(node->key, node->value.toNumber()); break; - case JSON_FLOAT: - writer.writeFloat(value->name, value->float_value); + case JSON_TRUE: + writer.writeBool(node->key, true); break; - case JSON_BOOL: - writer.writeBool(value->name, value->int_value != 0); + case JSON_FALSE: + writer.writeBool(node->key, false); break; case JSON_OBJECT: - writer.pushDict(value->name); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + writer.pushDict(node->key); + for (const JsonNode *it : node->value) { json_stringify_object(writer, it); } writer.pop(); break; case JSON_ARRAY: - writer.pushArray(value->name); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + writer.pushArray(node->key); + for (const JsonNode *it : node->value) { json_stringify_array(writer, it); } writer.pop(); @@ -305,34 +305,34 @@ static void json_stringify_object(JsonWriter &writer, const json_value *value) { } } -static void json_stringify_array(JsonWriter &writer, const json_value *value) { - switch (value->type) { +static void json_stringify_array(JsonWriter &writer, const JsonNode *node) { + switch (node->value.getTag()) { case JSON_NULL: writer.writeRaw("null"); break; case JSON_STRING: - writer.writeString(value->string_value); + writer.writeString(node->value.toString()); break; - case JSON_INT: - writer.writeInt(value->int_value); + case JSON_NUMBER: + writer.writeFloat(node->value.toNumber()); break; - case JSON_FLOAT: - writer.writeFloat(value->float_value); + case JSON_TRUE: + writer.writeBool(true); break; - case JSON_BOOL: - writer.writeBool(value->int_value != 0); + case JSON_FALSE: + writer.writeBool(false); break; case JSON_OBJECT: writer.pushDict(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_object(writer, it); } writer.pop(); break; case JSON_ARRAY: writer.pushArray(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_array(writer, it); } writer.pop(); diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index b2f4f30742..06afb66bc7 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -87,5 +87,5 @@ private: bool pretty_; }; -struct json_value; -std::string json_stringify(const json_value *json); +struct JsonNode; +std::string json_stringify(const JsonNode *json); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 8d43e4329d..556c328d4f 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -206,6 +206,7 @@ + @@ -220,8 +221,6 @@ - - @@ -238,6 +237,7 @@ + @@ -340,6 +340,7 @@ Default Default + @@ -683,8 +684,6 @@ - - @@ -699,6 +698,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index aecf223674..999519c3bd 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -242,12 +242,6 @@ ext\jpge - - ext\vjson - - - ext\vjson - gfx @@ -341,6 +335,12 @@ thin3d + + ext\gason + + + json + @@ -712,12 +712,6 @@ ext\jpge - - ext\vjson - - - ext\vjson - gfx @@ -814,6 +808,12 @@ thin3d + + ext\gason + + + json + @@ -876,14 +876,14 @@ {3721adbc-1a4a-4017-bc1d-893c7e86bc22} - - {c6910d82-73d3-4d97-ac9c-bcc03265b1bc} - {5ce64c0e-98e4-4411-86cc-aacaf2f60b21} {06c6305a-a646-485b-85b9-645a24dd6553} + + {5b740c6f-8dbc-4529-9114-6564b37b3548} + \ No newline at end of file diff --git a/libretro/Makefile.common b/libretro/Makefile.common index c732d9a1e4..d905f0a57a 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -125,8 +125,7 @@ SOURCES_CXX += \ SOURCES_CXX += $(EXTDIR)/xbrz/xbrz.cpp SOURCES_CXX += \ - $(NATIVEDIR)/ext/vjson/json.cpp \ - $(NATIVEDIR)/ext/vjson/block_allocator.cpp + $(NATIVEDIR)/ext/gason/gason.cpp SOURCES_CXX += $(NATIVEDIR)/ext/cityhash/city.cpp @@ -203,10 +202,11 @@ SOURCES_CXX += \ $(EXTDIR)/native/base/buffer.cpp \ $(EXTDIR)/native/base/colorutil.cpp \ $(EXTDIR)/native/base/display.cpp \ - $(EXTDIR)/native/base/logging.cpp \ + $(EXTDIR)/native/base/logging.cpp \ $(EXTDIR)/native/base/stringutil.cpp \ $(EXTDIR)/native/base/timeutil.cpp \ $(EXTDIR)/native/data/compression.cpp \ + $(EXTDIR)/native/json/json_reader.cpp \ $(EXTDIR)/glslang/OGLCompilersDLL/InitializeDll.cpp \ $(EXTDIR)/glslang/glslang/GenericCodeGen/CodeGen.cpp \ $(EXTDIR)/glslang/glslang/GenericCodeGen/Link.cpp \ From b56e3e8e9429682944fd8e6316f4a849f5772111 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 28 May 2018 19:30:50 -0700 Subject: [PATCH 137/301] Sas: Correct delay in playing samples. We still need to walk during the delay to "use it up." Need to test more to see if we can just walk once directly into ATTACK - might depend on pitch. This also makes the first play ignore the resampleHist, which matches samples to tests properly, and ignores linear interp for exact pitch. These changes fix all the sascore tests that used to work. --- Core/HW/SasAudio.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 9bf5fd82ef..ef33012dbe 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -508,15 +508,30 @@ void SasInstance::MixVoice(SasVoice &voice) { ERROR_LOG(SCESAS, "Too many samples to read (%d)! This shouldn't happen.", samplesToRead); samplesToRead = ARRAY_SIZE(mixTemp_) - 2; } - voice.ReadSamples(&mixTemp_[2], samplesToRead); - int tempPos = 2 + samplesToRead; + int readPos = 2; + if (voice.envelope.NeedsKeyOn()) { + readPos = 0; + samplesToRead += 2; + } + voice.ReadSamples(&mixTemp_[readPos], samplesToRead); + int tempPos = readPos + samplesToRead; + for (int i = 0; i < delay; ++i) { + // Walk the curve. This means we'll reach ATTACK already, likely. + // This matches the results of tests (but maybe we can just remove the STATE_KEYON_STEP hack.) + voice.envelope.Step(); + } + + const bool needsInterp = voicePitch != PSP_SAS_PITCH_BASE || (sampleFrac & PSP_SAS_PITCH_MASK) != 0; for (int i = delay; i < grainSize; i++) { const int16_t *s = mixTemp_ + (sampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); // Linear interpolation. Good enough. Need to make resampleHist bigger if we want more. - int f = sampleFrac & PSP_SAS_PITCH_MASK; - int sample = (s[0] * (PSP_SAS_PITCH_MASK - f) + s[1] * f) >> PSP_SAS_PITCH_BASE_SHIFT; + int sample = s[0]; + if (needsInterp) { + int f = sampleFrac & PSP_SAS_PITCH_MASK; + sample = (s[0] * (PSP_SAS_PITCH_MASK - f) + s[1] * f) >> PSP_SAS_PITCH_BASE_SHIFT; + } sampleFrac += voicePitch; // The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1. @@ -541,7 +556,7 @@ void SasInstance::MixVoice(SasVoice &voice) { voice.resampleHist[0] = mixTemp_[tempPos - 2]; voice.resampleHist[1] = mixTemp_[tempPos - 1]; - voice.sampleFrac = sampleFrac - (tempPos - 2) * PSP_SAS_PITCH_BASE;; + voice.sampleFrac = sampleFrac - (tempPos - 2) * PSP_SAS_PITCH_BASE; if (voice.HaveSamplesEnded()) voice.envelope.End(); From 289928a17020cd11cdd22fdb2b262db00d969ad8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 28 May 2018 19:32:40 -0700 Subject: [PATCH 138/301] Sas: Correct calculation of VAG end. Not sure why everything is negative one indexed, but this prevents reading beyond the size of the buffer. This shouldn't change sound output, but it may fix a crash if VAG is at the edge of memory (unlikely, though.) --- Core/HW/SasAudio.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index ef33012dbe..2758759f2d 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -65,6 +65,11 @@ void VagDecoder::Start(u32 data, u32 vagSize, bool loopEnabled) { } void VagDecoder::DecodeBlock(u8 *&read_pointer) { + if (curBlock_ == numBlocks_ - 1) { + end_ = true; + return; + } + u8 *readp = read_pointer; int predict_nr = *readp++; int shift_factor = predict_nr & 0xf; @@ -106,9 +111,6 @@ void VagDecoder::DecodeBlock(u8 *&read_pointer) { s_2 = s2; curSample = 0; curBlock_++; - if (curBlock_ == numBlocks_) { - end_ = true; - } read_pointer = readp; } From 4fb606839c1369691765e75b9b15d89f5976988e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 4 Jun 2018 20:54:25 -0700 Subject: [PATCH 139/301] UI: Add option to sort saves by size. It may be slow with many saves, but it's also very useful. It doesn't seem too bad as an option. --- UI/SavedataScreen.cpp | 105 +++++++++++++++++++++++++++++++++++++----- UI/SavedataScreen.h | 18 +++++++- 2 files changed, 109 insertions(+), 14 deletions(-) diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index fbce3e7862..ab39cc4b6f 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "base/colorutil.h" #include "base/timeutil.h" @@ -121,10 +122,37 @@ private: std::string savePath_; }; +class SortedLinearLayout : public UI::LinearLayout { +public: + typedef std::function CompareFunc; + + SortedLinearLayout(UI::Orientation orientation, UI::LayoutParams *layoutParams = nullptr) + : UI::LinearLayout(orientation, layoutParams) { + } + + void SetCompare(CompareFunc lessFunc) { + lessFunc_ = lessFunc; + } + + void Update() override; + +private: + CompareFunc lessFunc_; +}; + +void SortedLinearLayout::Update() { + if (lessFunc_) { + std::stable_sort(views_.begin(), views_.end(), lessFunc_); + } + UI::LinearLayout::Update(); +} + class SavedataButton : public UI::Clickable { public: SavedataButton(const std::string &gamePath, UI::LayoutParams *layoutParams = 0) - : UI::Clickable(layoutParams), savePath_(gamePath) {} + : UI::Clickable(layoutParams), savePath_(gamePath) { + SetTag(gamePath); + } void Draw(UIContext &dc) override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override { @@ -134,8 +162,6 @@ public: const std::string &GamePath() const { return savePath_; } - std::string GetGamePath() const { return savePath_; } - private: std::string savePath_; std::string title_; @@ -279,10 +305,40 @@ void SavedataButton::Draw(UIContext &dc) { } SavedataBrowser::SavedataBrowser(std::string path, UI::LayoutParams *layoutParams) - : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), gameList_(0), path_(path) { + : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path) { Refresh(); } +void SavedataBrowser::SetSortOption(SavedataSortOption opt) { + sortOption_ = opt; + if (gameList_) { + SortedLinearLayout *gl = static_cast(gameList_); + if (sortOption_ == SavedataSortOption::FILENAME) { + gl->SetCompare(&ByFilename); + } else if (sortOption_ == SavedataSortOption::SIZE) { + gl->SetCompare(&BySize); + } + } +} + +bool SavedataBrowser::ByFilename(const UI::View *v1, const UI::View *v2) { + const SavedataButton *b1 = static_cast(v1); + const SavedataButton *b2 = static_cast(v2); + + return strcmp(b1->GamePath().c_str(), b2->GamePath().c_str()) < 0; +} + +bool SavedataBrowser::BySize(const UI::View *v1, const UI::View *v2) { + const SavedataButton *b1 = static_cast(v1); + const SavedataButton *b2 = static_cast(v2); + + std::shared_ptr g1info = g_gameInfoCache->GetInfo(nullptr, b1->GamePath(), GAMEINFO_WANTSIZE); + std::shared_ptr g2info = g_gameInfoCache->GetInfo(nullptr, b2->GamePath(), GAMEINFO_WANTSIZE); + + // They might be zero, but that's fine. + return g1info->gameSize > g2info->gameSize; +} + void SavedataBrowser::Refresh() { using namespace UI; @@ -293,7 +349,7 @@ void SavedataBrowser::Refresh() { I18NCategory *mm = GetI18NCategory("MainMenu"); I18NCategory *sa = GetI18NCategory("Savedata"); - UI::LinearLayout *gl = new UI::LinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); + SortedLinearLayout *gl = new SortedLinearLayout(UI::ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); gl->SetSpacing(4.0f); gameList_ = gl; Add(gameList_); @@ -327,6 +383,9 @@ void SavedataBrowser::Refresh() { group->Add(new TextView(sa->T("None yet. Things will appear here after you save."))); gameList_->Add(group); } + + // Reapply. + SetSortOption(sortOption_); } UI::EventReturn SavedataBrowser::SavedataButtonClick(UI::EventParams &e) { @@ -357,25 +416,47 @@ void SavedataScreen::CreateViews() { std::string savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE); gridStyle_ = false; - root_ = new LinearLayout(ORIENT_VERTICAL); + root_ = new AnchorLayout(); + + LinearLayout *main = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); TabHolder *tabs = new TabHolder(ORIENT_HORIZONTAL, 64, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); tabs->SetTag("Savedata"); ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scroll->SetTag("SavedataBrowser"); - browser_ = scroll->Add(new SavedataBrowser(savedata_dir, new LayoutParams(FILL_PARENT, FILL_PARENT))); - browser_->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); + dataBrowser_ = scroll->Add(new SavedataBrowser(savedata_dir, new LayoutParams(FILL_PARENT, FILL_PARENT))); + dataBrowser_->SetSortOption(sortOption_); + dataBrowser_->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); tabs->AddTab(sa->T("Save Data"), scroll); ScrollView *scroll2 = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scroll2->SetTag("SavedataStatesBrowser"); - SavedataBrowser *browser2 = scroll2->Add(new SavedataBrowser(savestate_dir)); - browser2->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); + stateBrowser_ = scroll2->Add(new SavedataBrowser(savestate_dir)); + stateBrowser_->SetSortOption(sortOption_); + stateBrowser_->OnChoice.Handle(this, &SavedataScreen::OnSavedataButtonClick); tabs->AddTab(sa->T("Save States"), scroll2); - root_->Add(tabs); - root_->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); + main->Add(tabs); + main->Add(new Button(di->T("Back"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 0.0f)))->OnClick.Handle(this, &UIScreen::OnBack); + + ChoiceStrip *sortStrip = new ChoiceStrip(ORIENT_HORIZONTAL, new AnchorLayoutParams(NONE, 0, 0, NONE)); + sortStrip->AddChoice(sa->T("Filename")); + sortStrip->AddChoice(sa->T("Size")); + sortStrip->SetSelection((int)sortOption_); + sortStrip->OnChoice.Handle(this, &SavedataScreen::OnSortClick); + + root_->Add(main); + root_->Add(sortStrip); +} + +UI::EventReturn SavedataScreen::OnSortClick(UI::EventParams &e) { + sortOption_ = SavedataSortOption(e.a); + + dataBrowser_->SetSortOption(sortOption_); + stateBrowser_->SetSortOption(sortOption_); + + return UI::EVENT_DONE; } UI::EventReturn SavedataScreen::OnSavedataButtonClick(UI::EventParams &e) { diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index adf688013c..d35b4f8920 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -26,17 +26,28 @@ #include "UI/MiscScreens.h" +enum class SavedataSortOption { + FILENAME, + SIZE, +}; + class SavedataBrowser : public UI::LinearLayout { public: SavedataBrowser(std::string path, UI::LayoutParams *layoutParams = 0); + void SetSortOption(SavedataSortOption opt); + UI::Event OnChoice; private: + static bool ByFilename(const UI::View *, const UI::View *); + static bool BySize(const UI::View *, const UI::View *); + void Refresh(); UI::EventReturn SavedataButtonClick(UI::EventParams &e); - UI::ViewGroup *gameList_; + SavedataSortOption sortOption_ = SavedataSortOption::FILENAME; + UI::ViewGroup *gameList_ = nullptr; std::string path_; }; @@ -50,7 +61,10 @@ public: protected: UI::EventReturn OnSavedataButtonClick(UI::EventParams &e); + UI::EventReturn OnSortClick(UI::EventParams &e); void CreateViews() override; bool gridStyle_; - SavedataBrowser *browser_; + SavedataSortOption sortOption_ = SavedataSortOption::FILENAME; + SavedataBrowser *dataBrowser_; + SavedataBrowser *stateBrowser_; }; From f0be2d5f88470b3806c2f5d9707a3038bb6599c7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 4 Jun 2018 21:02:40 -0700 Subject: [PATCH 140/301] UI: Quit sorting when done. This will prevent keeping the gameInfo objects alive unnecessarily. --- UI/SavedataScreen.cpp | 17 ++++++++++++++--- UI/SavedataScreen.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index ab39cc4b6f..37f767a82d 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -23,6 +23,7 @@ #include "gfx_es2/draw_buffer.h" #include "i18n/i18n.h" #include "math/curves.h" +#include "thread/prioritizedworkqueue.h" #include "util/text/utf8.h" #include "ui/ui_context.h" #include "ui/view.h" @@ -125,12 +126,13 @@ private: class SortedLinearLayout : public UI::LinearLayout { public: typedef std::function CompareFunc; + typedef std::function DoneFunc; SortedLinearLayout(UI::Orientation orientation, UI::LayoutParams *layoutParams = nullptr) : UI::LinearLayout(orientation, layoutParams) { } - void SetCompare(CompareFunc lessFunc) { + void SetCompare(CompareFunc lessFunc, DoneFunc) { lessFunc_ = lessFunc; } @@ -138,12 +140,16 @@ public: private: CompareFunc lessFunc_; + DoneFunc doneFunc_; }; void SortedLinearLayout::Update() { if (lessFunc_) { std::stable_sort(views_.begin(), views_.end(), lessFunc_); } + if (doneFunc_ && doneFunc_()) { + lessFunc_ = CompareFunc(); + } UI::LinearLayout::Update(); } @@ -314,9 +320,9 @@ void SavedataBrowser::SetSortOption(SavedataSortOption opt) { if (gameList_) { SortedLinearLayout *gl = static_cast(gameList_); if (sortOption_ == SavedataSortOption::FILENAME) { - gl->SetCompare(&ByFilename); + gl->SetCompare(&ByFilename, &SortDone); } else if (sortOption_ == SavedataSortOption::SIZE) { - gl->SetCompare(&BySize); + gl->SetCompare(&BySize, &SortDone); } } } @@ -339,6 +345,11 @@ bool SavedataBrowser::BySize(const UI::View *v1, const UI::View *v2) { return g1info->gameSize > g2info->gameSize; } +bool SavedataBrowser::SortDone() { + PrioritizedWorkQueue *wq = g_gameInfoCache->WorkQueue(); + return wq->Done(); +} + void SavedataBrowser::Refresh() { using namespace UI; diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index d35b4f8920..0ab3e6ed56 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -42,6 +42,7 @@ public: private: static bool ByFilename(const UI::View *, const UI::View *); static bool BySize(const UI::View *, const UI::View *); + static bool SortDone(); void Refresh(); UI::EventReturn SavedataButtonClick(UI::EventParams &e); From c15ade3b1be7ac833d63aae5e5e15d36bfc86f32 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 18:08:41 -0700 Subject: [PATCH 141/301] Core: Refactor stepping and locking. This should fix some race conditions with the cond vars. --- Core/Core.cpp | 71 +++++++++++++++++++++++++++------------------------ Core/Core.h | 2 ++ Core/Host.h | 2 -- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 5bf6793764..3c9bf93018 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -87,7 +87,6 @@ void Core_NotifyLifecycle(CoreLifecycle stage) { void Core_Stop() { Core_UpdateState(CORE_POWERDOWN); - m_StepCond.notify_all(); } bool Core_IsStepping() { @@ -173,6 +172,7 @@ bool UpdateScreenScale(int width, int height) { return false; } +// Note: not used on Android. void UpdateRunLoop() { if (windowHidden && g_Config.bPauseWhenMinimized) { sleep_ms(16); @@ -227,11 +227,13 @@ void Core_RunLoop(GraphicsContext *ctx) { } void Core_DoSingleStep() { + std::lock_guard guard(m_hStepMutex); singleStepPending = true; m_StepCond.notify_all(); } void Core_UpdateSingleStep() { + std::lock_guard guard(m_hStepMutex); m_StepCond.notify_all(); } @@ -241,17 +243,49 @@ void Core_SingleStep() { static inline void CoreStateProcessed() { if (coreStatePending) { + std::lock_guard guard(m_hInactiveMutex); coreStatePending = false; m_InactiveCond.notify_all(); } } +static inline void Core_WaitStepping() { + std::unique_lock guard(m_hStepMutex); + if (!singleStepPending && coreState == CORE_STEPPING) + m_StepCond.wait(guard); +} + +void Core_ProcessStepping() { + CoreStateProcessed(); + + // Check if there's any pending save state actions. + SaveState::Process(); + if (coreState != CORE_STEPPING) { + return; + } + + host->UpdateDisassembly(); + host->UpdateMemView(); + + // Need to check inside the lock to avoid races. + Core_WaitStepping(); + + // We may still be stepping without singleStepPending to process a save state. + if (singleStepPending && coreState == CORE_STEPPING) { + singleStepPending = false; + + Core_SingleStep(); + // Update disasm dialog. + host->UpdateDisassembly(); + host->UpdateMemView(); + } +} + // Many platforms, like Android, do not call this function but handle things on their own. // Instead they simply call NativeRender and NativeUpdate directly. void Core_Run(GraphicsContext *ctx) { host->UpdateDisassembly(); while (true) { -reswitch: if (GetUIState() != UISTATE_INGAME) { CoreStateProcessed(); if (GetUIState() == UISTATE_EXIT) { @@ -270,38 +304,9 @@ reswitch: // We should never get here on Android. case CORE_STEPPING: - singleStepPending = false; - CoreStateProcessed(); - - // Check if there's any pending savestate actions. - SaveState::Process(); - if (coreState == CORE_POWERDOWN) { + Core_ProcessStepping(); + if (coreState == CORE_POWERDOWN) return; - } - - // wait for step command.. - host->UpdateDisassembly(); - host->UpdateMemView(); - host->SendCoreWait(true); - - { - std::unique_lock guard(m_hStepMutex); - m_StepCond.wait(guard); - } - - host->SendCoreWait(false); - // No step pending? Let's go back to the wait. - if (!singleStepPending || coreState != CORE_STEPPING) { - if (coreState == CORE_POWERDOWN) { - return; - } - goto reswitch; - } - - Core_SingleStep(); - // update disasm dialog - host->UpdateDisassembly(); - host->UpdateMemView(); break; case CORE_POWERUP: diff --git a/Core/Core.h b/Core/Core.h index f01a9d03e8..84fe707792 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -34,6 +34,7 @@ void Core_SetGraphicsContext(GraphicsContext *ctx); void Core_EnableStepping(bool step); void Core_DoSingleStep(); void Core_UpdateSingleStep(); +void Core_ProcessStepping(); // Changes every time we enter stepping. int Core_GetSteppingCounter(); @@ -54,6 +55,7 @@ bool Core_IsStepping(); bool Core_IsActive(); bool Core_IsInactive(); +// Warning: these currently work only on Windows. void Core_WaitInactive(); void Core_WaitInactive(int milliseconds); diff --git a/Core/Host.h b/Core/Host.h index c7eed94de1..b06473c4dc 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -50,8 +50,6 @@ public: virtual void SaveSymbolMap() {} virtual void SetWindowTitle(const char *message) {} - virtual void SendCoreWait(bool) {} - // While debugging is active, it's perfectly fine for these to block. virtual bool GPUDebuggingActive() { return false; } virtual void GPUNotifyCommand(u32 pc) {} From 2e2d8f29893fdadf5d81e2cee77f49c724ad41b0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 18:38:17 -0700 Subject: [PATCH 142/301] Core: Process stepping on other platforms. This includes Android. --- Core/Core.cpp | 24 +++++++++++++----------- Core/System.cpp | 14 ++++++++++++++ Core/System.h | 1 + UI/EmuScreen.cpp | 9 +-------- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 3c9bf93018..2370f9d603 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -239,6 +239,8 @@ void Core_UpdateSingleStep() { void Core_SingleStep() { currentMIPS->SingleStep(); + if (coreState == CORE_STEPPING) + steppingCounter++; } static inline void CoreStateProcessed() { @@ -249,10 +251,14 @@ static inline void CoreStateProcessed() { } } -static inline void Core_WaitStepping() { +static inline bool Core_WaitStepping() { std::unique_lock guard(m_hStepMutex); if (!singleStepPending && coreState == CORE_STEPPING) m_StepCond.wait(guard); + + bool result = singleStepPending; + singleStepPending = false; + return result; } void Core_ProcessStepping() { @@ -268,12 +274,10 @@ void Core_ProcessStepping() { host->UpdateMemView(); // Need to check inside the lock to avoid races. - Core_WaitStepping(); + bool doStep = Core_WaitStepping(); // We may still be stepping without singleStepPending to process a save state. - if (singleStepPending && coreState == CORE_STEPPING) { - singleStepPending = false; - + if (doStep && coreState == CORE_STEPPING) { Core_SingleStep(); // Update disasm dialog. host->UpdateDisassembly(); @@ -298,15 +302,13 @@ void Core_Run(GraphicsContext *ctx) { switch (coreState) { case CORE_RUNNING: + case CORE_STEPPING: // enter a fast runloop Core_RunLoop(ctx); - break; - - // We should never get here on Android. - case CORE_STEPPING: - Core_ProcessStepping(); - if (coreState == CORE_POWERDOWN) + if (coreState == CORE_POWERDOWN) { + CoreStateProcessed(); return; + } break; case CORE_POWERUP: diff --git a/Core/System.cpp b/Core/System.cpp index afe1ac76ea..6355c66dd3 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -430,10 +430,24 @@ void PSP_EndHostFrame() { } } +void PSP_RunLoopWhileState() { + // We just run the CPU until we get to vblank. This will quickly sync up pretty nicely. + // The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully... + int blockTicks = usToCycles(1000000 / 10); + + // Run until CORE_NEXTFRAME + while (coreState == CORE_RUNNING || coreState == CORE_STEPPING) { + PSP_RunLoopFor(blockTicks); + } +} + void PSP_RunLoopUntil(u64 globalticks) { SaveState::Process(); if (coreState == CORE_POWERDOWN || coreState == CORE_ERROR) { return; + } else if (coreState == CORE_STEPPING) { + Core_ProcessStepping(); + return; } mipsr4k.RunLoopUntil(globalticks); diff --git a/Core/System.h b/Core/System.h index a2a3ed5c48..32182c3bc2 100644 --- a/Core/System.h +++ b/Core/System.h @@ -70,6 +70,7 @@ void PSP_Shutdown(); void PSP_BeginHostFrame(); void PSP_EndHostFrame(); +void PSP_RunLoopWhileState(); void PSP_RunLoopUntil(u64 globalticks); void PSP_RunLoopFor(int cycles); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 8064883ada..35cf6a0e0e 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1193,14 +1193,7 @@ void EmuScreen::render() { PSP_BeginHostFrame(); - // We just run the CPU until we get to vblank. This will quickly sync up pretty nicely. - // The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully... - int blockTicks = usToCycles(1000000 / 10); - - // Run until CORE_NEXTFRAME - while (coreState == CORE_RUNNING) { - PSP_RunLoopFor(blockTicks); - } + PSP_RunLoopWhileState(); // Hopefully coreState is now CORE_NEXTFRAME if (coreState == CORE_NEXTFRAME) { From 3c8cc5ab6488e63c7d15d691bddfe18331538e51 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 22:01:11 -0700 Subject: [PATCH 143/301] Debugger: Fix initialization issues. Sometimes temp breakpoints had log set by accident. --- Core/Debugger/Breakpoints.cpp | 5 ---- Core/Debugger/Breakpoints.h | 46 +++++++++++++---------------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 9f7a27d39f..597d2a8f2d 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -33,11 +33,6 @@ u64 CBreakPoints::breakSkipFirstTicks_ = 0; std::vector CBreakPoints::memChecks_; std::vector CBreakPoints::cleanupMemChecks_; -MemCheck::MemCheck() -{ - numHits = 0; -} - void MemCheck::Log(u32 addr, bool write, int size, u32 pc) { if (result & BREAK_ACTION_LOG) { if (logFormat.empty()) { diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 934667914a..2457e45aca 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -21,8 +21,7 @@ #include "Core/Debugger/DebugInterface.h" -enum BreakAction -{ +enum BreakAction { BREAK_ACTION_IGNORE = 0x00, BREAK_ACTION_LOG = 0x01, BREAK_ACTION_PAUSE = 0x02, @@ -37,35 +36,27 @@ static inline BreakAction operator | (const BreakAction &lhs, const BreakAction return BreakAction((u32)lhs | (u32)rhs); } -struct BreakPointCond -{ - DebugInterface *debug; +struct BreakPointCond { + DebugInterface *debug = nullptr; PostfixExpression expression; std::string expressionString; - BreakPointCond() : debug(nullptr) - { - } - - u32 Evaluate() - { + u32 Evaluate() { u32 result; - if (debug->parseExpression(expression,result) == false) return 0; + if (debug->parseExpression(expression, result) == false) + return 0; return result; } }; -struct BreakPoint -{ - BreakPoint() : hasCond(false) {} - +struct BreakPoint { u32 addr; bool temporary; - BreakAction result; + BreakAction result = BREAK_ACTION_IGNORE; std::string logFormat; - bool hasCond; + bool hasCond = false; BreakPointCond cond; bool IsEnabled() const { @@ -80,8 +71,7 @@ struct BreakPoint } }; -enum MemCheckCondition -{ +enum MemCheckCondition { MEMCHECK_READ = 0x01, MEMCHECK_WRITE = 0x02, MEMCHECK_WRITE_ONCHANGE = 0x04, @@ -89,21 +79,19 @@ enum MemCheckCondition MEMCHECK_READWRITE = 0x03, }; -struct MemCheck -{ - MemCheck(); +struct MemCheck { u32 start; u32 end; - MemCheckCondition cond; - BreakAction result; + MemCheckCondition cond = MEMCHECK_READ; + BreakAction result = BREAK_ACTION_IGNORE; std::string logFormat; - u32 numHits; + u32 numHits = 0; - u32 lastPC; - u32 lastAddr; - int lastSize; + u32 lastPC = 0; + u32 lastAddr = 0; + int lastSize = 0; BreakAction Action(u32 addr, bool write, int size, u32 pc); void JitBefore(u32 addr, bool write, int size, u32 pc); From 97b0c170380521ad01155c1c751429f01b245d29 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 18:59:48 -0700 Subject: [PATCH 144/301] Core: Wake up inactive waits on game exit. Fixes menu hangs since the refactor. --- Core/Core.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 2370f9d603..84225b8451 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -101,6 +101,14 @@ bool Core_IsInactive() { return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending; } +static inline void Core_StateProcessed() { + if (coreStatePending) { + std::lock_guard guard(m_hInactiveMutex); + coreStatePending = false; + m_InactiveCond.notify_all(); + } +} + void Core_WaitInactive() { while (Core_IsActive()) { std::unique_lock guard(m_hInactiveMutex); @@ -191,6 +199,8 @@ void KeepScreenAwake() { void Core_RunLoop(GraphicsContext *ctx) { graphicsContext = ctx; while ((GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT) { + // In case it was pending, we're not in game anymore. We won't get to Core_Run(). + Core_StateProcessed(); time_update(); double startTime = time_now_d(); UpdateRunLoop(); @@ -243,14 +253,6 @@ void Core_SingleStep() { steppingCounter++; } -static inline void CoreStateProcessed() { - if (coreStatePending) { - std::lock_guard guard(m_hInactiveMutex); - coreStatePending = false; - m_InactiveCond.notify_all(); - } -} - static inline bool Core_WaitStepping() { std::unique_lock guard(m_hStepMutex); if (!singleStepPending && coreState == CORE_STEPPING) @@ -262,7 +264,7 @@ static inline bool Core_WaitStepping() { } void Core_ProcessStepping() { - CoreStateProcessed(); + Core_StateProcessed(); // Check if there's any pending save state actions. SaveState::Process(); @@ -291,7 +293,7 @@ void Core_Run(GraphicsContext *ctx) { host->UpdateDisassembly(); while (true) { if (GetUIState() != UISTATE_INGAME) { - CoreStateProcessed(); + Core_StateProcessed(); if (GetUIState() == UISTATE_EXIT) { UpdateRunLoop(); return; @@ -306,7 +308,7 @@ void Core_Run(GraphicsContext *ctx) { // enter a fast runloop Core_RunLoop(ctx); if (coreState == CORE_POWERDOWN) { - CoreStateProcessed(); + Core_StateProcessed(); return; } break; @@ -315,7 +317,7 @@ void Core_Run(GraphicsContext *ctx) { case CORE_POWERDOWN: case CORE_ERROR: // Exit loop!! - CoreStateProcessed(); + Core_StateProcessed(); return; From ffa4c18f6c0605f90e2ec508367a63a608aba4d9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 20:40:09 -0700 Subject: [PATCH 145/301] Core: Remove sleep on stepping. This doesn't seem needed and has been there since day 1. --- Core/Core.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 84225b8451..67dffa8586 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -329,7 +329,6 @@ void Core_Run(GraphicsContext *ctx) { void Core_EnableStepping(bool step) { if (step) { - sleep_ms(1); host->SetDebugMode(true); Core_UpdateState(CORE_STEPPING); steppingCounter++; From 395ac32d6342de5185e0ed780e6e70fa6f291395 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 1 May 2018 18:11:57 -0700 Subject: [PATCH 146/301] Debugger: Run memory breakpoints on mobile. --- Core/HLE/ReplaceTables.cpp | 29 +++++++++++++---------------- Core/HLE/sceKernelInterrupt.cpp | 4 ++-- Core/HW/MediaEngine.cpp | 14 ++------------ Core/MemMap.cpp | 3 +-- Core/MemMapHelpers.h | 6 ------ GPU/GPUCommon.cpp | 2 -- GPU/Null/NullGpu.cpp | 2 -- GPU/Software/SoftGpu.cpp | 2 -- 8 files changed, 18 insertions(+), 44 deletions(-) diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 8dd52b2dcd..dec3f24636 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -152,10 +152,10 @@ static int Replace_memcpy() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -194,10 +194,10 @@ static int Replace_memcpy_jak() { currentMIPS->r[MIPS_REG_A2] = 0; currentMIPS->r[MIPS_REG_A3] = destPtr + bytes; RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop.. } @@ -222,10 +222,10 @@ static int Replace_memcpy16() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -260,10 +260,10 @@ static int Replace_memcpy_swizzled() { } RETURN(0); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, pitch * h, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, pitch * h, currentMIPS->pc); -#endif + return 10 + (pitch * h) / 4; // approximation } @@ -288,10 +288,10 @@ static int Replace_memmove() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc); CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -310,9 +310,9 @@ static int Replace_memset() { } } RETURN(destPtr); -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 10 + bytes / 4; // approximation } @@ -342,9 +342,8 @@ static int Replace_memset_jak() { currentMIPS->r[MIPS_REG_A3] = -1; RETURN(destPtr); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc); -#endif + return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..) } @@ -591,11 +590,9 @@ static int Replace_dl_write_matrix() { #endif } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(PARAM(2), false, count * sizeof(float), currentMIPS->pc); CBreakPoints::ExecMemCheck(PARAM(0) + 2 * sizeof(u32), true, sizeof(u32), currentMIPS->pc); CBreakPoints::ExecMemCheck(dlStruct[2], true, (count + 1) * sizeof(u32), currentMIPS->pc); -#endif dlStruct[2] += (1 + count) * 4; RETURN(dlStruct[2]); diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index ba9d7c1588..29c0cc5c7f 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -654,10 +654,10 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) *dstp++ = *srcp++; } } -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(src, false, size, currentMIPS->pc); CBreakPoints::ExecMemCheck(dst, true, size, currentMIPS->pc); -#endif + return dst; } diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index cf82e94e1c..1da8800240 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -766,9 +766,8 @@ int MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMo delete [] imgbuf; } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr, true, videoImageSize, currentMIPS->pc); -#endif + return videoImageSize; #endif // USE_FFMPEG return 0; @@ -822,9 +821,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineRGBA(imgbuf, data, width); data += m_desWidth * sizeof(u32); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u32), true, width * sizeof(u32), currentMIPS->pc); -#endif } break; @@ -834,9 +831,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5650(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); -#endif } break; @@ -846,9 +841,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5551(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); -#endif } break; @@ -858,9 +851,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR4444(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += videoLineSize; -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); -#endif } break; @@ -954,9 +945,8 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { if (!m_audioContext->Decode(audioFrame, frameSize, buffer, &outbytes)) { ERROR_LOG(ME, "Audio (%s) decode failed during video playback", GetCodecName(m_audioType)); } -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr, true, outbytes, currentMIPS->pc); -#endif } return 0x2000; diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index eb5d49ab14..8282aac3db 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -446,9 +446,8 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) { for (size_t i = 0; i < _iLength; i++) Write_U8(_iValue, (u32)(_Address + i)); } -#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc); -#endif } } // namespace diff --git a/Core/MemMapHelpers.h b/Core/MemMapHelpers.h index 1fe06d3ae1..9431db5c0d 100644 --- a/Core/MemMapHelpers.h +++ b/Core/MemMapHelpers.h @@ -33,9 +33,7 @@ inline void Memcpy(const u32 to_address, const void *from_data, const u32 len) u8 *to = GetPointer(to_address); if (to) { memcpy(to, from_data, len); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc); -#endif } // if not, GetPointer will log. } @@ -45,9 +43,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len) const u8 *from = GetPointer(from_address); if (from) { memcpy(to_data, from, len); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(from_address, false, len, currentMIPS->pc); -#endif } // if not, GetPointer will log. } @@ -55,9 +51,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len) inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len) { Memcpy(GetPointer(to_address), from_address, len); -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc); -#endif } void Memset(const u32 _Address, const u8 _Data, const u32 _iLength); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1950eab058..dd4c4ad0a0 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2576,10 +2576,8 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason); } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); CBreakPoints::ExecMemCheck(dstBasePtr + (dstY * dstStride + dstX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); -#endif // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index a240840496..29a50f2054 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -344,10 +344,8 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) { memcpy(dst, src, width * bpp); } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); -#endif // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index b24d286940..9432ba97ef 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -648,10 +648,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { memcpy(dst, src, width * bpp); } -#ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc); CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc); -#endif // TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate. cyclesExecuted += ((height * width * bpp) * 16) / 10; From eb4b59b5301052d3b7cd84057a9a5fa25c37cb82 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 1 May 2018 22:18:33 -0700 Subject: [PATCH 147/301] arm64jit: Enable breakpoints. Memory breakpoints not yet really tested. --- Core/Core.cpp | 2 + Core/Debugger/Breakpoints.cpp | 21 +++-- Core/Debugger/Breakpoints.h | 2 +- Core/MIPS/ARM64/Arm64CompFPU.cpp | 2 +- Core/MIPS/ARM64/Arm64CompLoadStore.cpp | 3 + Core/MIPS/ARM64/Arm64CompVFPU.cpp | 2 + Core/MIPS/ARM64/Arm64Jit.cpp | 113 ++++++++++++++++++++++++- Core/MIPS/ARM64/Arm64Jit.h | 2 + Core/MIPS/x86/JitSafeMem.cpp | 11 +-- Windows/Debugger/Debugger_Disasm.cpp | 3 +- 10 files changed, 136 insertions(+), 25 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 67dffa8586..5e32d29af4 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -272,6 +272,8 @@ void Core_ProcessStepping() { return; } + // We're not inside jit now, so it's safe to clear the breakpoints. + CBreakPoints::ClearTemporaryBreakPoints(); host->UpdateDisassembly(); host->UpdateMemView(); diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 597d2a8f2d..0223a2f6fc 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -488,17 +488,20 @@ u32 CBreakPoints::CheckSkipFirst() return 0; } -const std::vector CBreakPoints::GetMemCheckRanges() -{ +const std::vector CBreakPoints::GetMemCheckRanges(bool write) { std::vector ranges = memChecks_; - for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it) - { - MemCheck check = *it; + for (const auto &check : memChecks_) { + if (!(check.cond & MEMCHECK_READ) && !write) + continue; + if (!(check.cond & MEMCHECK_WRITE) && write) + continue; + + MemCheck copy = check; // Toggle the cached part of the address. - check.start ^= 0x40000000; - if (check.end != 0) - check.end ^= 0x40000000; - ranges.push_back(check); + copy.start ^= 0x40000000; + if (copy.end != 0) + copy.end ^= 0x40000000; + ranges.push_back(copy); } return ranges; diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 2457e45aca..8cd7acdc4d 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -156,7 +156,7 @@ public: static u32 CheckSkipFirst(); // Includes uncached addresses. - static const std::vector GetMemCheckRanges(); + static const std::vector GetMemCheckRanges(bool write); static const std::vector GetMemChecks(); static const std::vector GetBreakpoints(); diff --git a/Core/MIPS/ARM64/Arm64CompFPU.cpp b/Core/MIPS/ARM64/Arm64CompFPU.cpp index e608baa6d4..55298ae577 100644 --- a/Core/MIPS/ARM64/Arm64CompFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompFPU.cpp @@ -81,6 +81,7 @@ void Arm64Jit::Comp_FPU3op(MIPSOpcode op) { void Arm64Jit::Comp_FPULS(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); // Surprisingly, these work fine alraedy. @@ -88,7 +89,6 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op) int ft = _FT; MIPSGPReg rs = _RS; // u32 addr = R(rs) + offset; - // logBlocks = 1; std::vector skips; switch (op >> 26) { case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1 diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index 4e5f42f053..7b962f0e95 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -111,6 +111,7 @@ namespace MIPSComp { void Arm64Jit::Comp_ITypeMemLR(MIPSOpcode op, bool load) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFF); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -119,6 +120,7 @@ namespace MIPSComp { if (!js.inDelaySlot) { // Optimisation: Combine to single unaligned load/store bool isLeft = (o == 34 || o == 42); + CheckMemoryBreakpoint(1); MIPSOpcode nextOp = GetOffsetInstruction(1); // Find a matching shift in opposite direction with opposite offset. if (nextOp == (isLeft ? (op.encoding + (4 << 26) - 3) : (op.encoding - (4 << 26) + 3))) { @@ -255,6 +257,7 @@ namespace MIPSComp { void Arm64Jit::Comp_ITypeMem(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFF); bool load = false; diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 0277df28e7..7799dfbfa4 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -202,6 +202,7 @@ namespace MIPSComp { void Arm64Jit::Comp_SV(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); s32 offset = (signed short)(op & 0xFFFC); int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); @@ -275,6 +276,7 @@ namespace MIPSComp { void Arm64Jit::Comp_SVQ(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int imm = (signed short)(op&0xFFFC); int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); diff --git a/Core/MIPS/ARM64/Arm64Jit.cpp b/Core/MIPS/ARM64/Arm64Jit.cpp index 46b1a9bf3e..afadca29e0 100644 --- a/Core/MIPS/ARM64/Arm64Jit.cpp +++ b/Core/MIPS/ARM64/Arm64Jit.cpp @@ -28,6 +28,7 @@ #include "Core/Config.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" @@ -44,7 +45,7 @@ using namespace Arm64JitConstants; -void DisassembleArm64Print(const u8 *data, int size) { +static void DisassembleArm64Print(const u8 *data, int size) { std::vector lines = DisassembleArm64(data, size); for (auto s : lines) { ILOG("%s", s.c_str()); @@ -60,6 +61,33 @@ void DisassembleArm64Print(const u8 *data, int size) { ILOG("===");*/ } +static u32 JitBreakpoint() { + // Should we skip this breakpoint? + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc); + if ((result & BREAK_ACTION_PAUSE) == 0) + return 0; + + return 1; +} + +static u32 JitMemCheck(u32 pc) { + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + // Note: pc may be the delay slot. + const auto op = Memory::Read_Instruction(pc, true); + s32 offset = (s16)(op & 0xFFFF); + if (MIPSGetInfo(op) & IS_VFPU) + offset &= 0xFFFC; + u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset; + + CBreakPoints::ExecOpMemCheck(addr, pc); + return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1; +} + namespace MIPSComp { using namespace Arm64Gen; @@ -80,6 +108,10 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f GenerateFixedCode(jo); js.startDefaultPrefix = mips_->HasDefaultPrefix(); js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3]; + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } Arm64Jit::~Arm64Jit() { @@ -99,6 +131,10 @@ void Arm64Jit::DoState(PointerWrap &p) { } // Note: we can't update the currentRoundingFunc here because fcr31 wasn't loaded yet. + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } void Arm64Jit::UpdateFCR31() { @@ -151,12 +187,16 @@ void Arm64Jit::EatInstruction(MIPSOpcode op) { ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot."); } + CheckJitBreakpoint(GetCompilerPC() + 4, 0); js.numInstructions++; js.compilerPC += 4; js.downcountAmount += MIPSGetInstructionCycleEstimate(op); } void Arm64Jit::CompileDelaySlot(int flags) { + // Need to offset the downcount which was already incremented for the branch + delay slot. + CheckJitBreakpoint(GetCompilerPC() + 4, -2); + // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. @@ -285,8 +325,10 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) { js.numInstructions = 0; while (js.compiling) { gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages + // Jit breakpoints are quite fast, so let's do them in release too. + CheckJitBreakpoint(GetCompilerPC(), 0); + MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC()); - js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); MIPSCompileOp(inst, this); @@ -473,7 +515,18 @@ void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op) return; } - if (entry->flags & REPFLAG_DISABLED) { + u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC()); + bool disabled = (entry->flags & REPFLAG_DISABLED) != 0; + if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) { + // We don't need to disable hooks, the code will still run. + if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) { + // Any breakpoint at the func entry was already tripped, so we can still run the replacement. + // That's a common case - just to see how often the replacement hits. + disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32)); + } + } + + if (disabled) { MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this); } else if (entry->jitReplaceFunc) { MIPSReplaceFunc repl = entry->jitReplaceFunc; @@ -646,6 +699,60 @@ void Arm64Jit::WriteSyscallExit() { B((const void *)dispatcherCheckCoreState); } +bool Arm64Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) { + if (CBreakPoints::IsAddressBreakPoint(addr)) { + MRS(FLAGTEMPREG, FIELD_NZCV); + FlushAll(); + MOVI2R(SCRATCH1, GetCompilerPC()); + MovToPC(SCRATCH1); + RestoreRoundingMode(); + QuickCallFunction(SCRATCH1_64, &JitBreakpoint); + + // If 0, the conditional breakpoint wasn't taken. + CMPI2R(W0, 0); + FixupBranch skip = B(CC_EQ); + WriteDownCount(downcountOffset); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(FIELD_NZCV, FLAGTEMPREG); + return true; + } + + return false; +} + +bool Arm64Jit::CheckMemoryBreakpoint(int instructionOffset) { + if (CBreakPoints::HasMemChecks()) { + int off = instructionOffset + (js.inDelaySlot ? 1 : 0); + + MRS(FLAGTEMPREG, FIELD_NZCV); + FlushAll(); + RestoreRoundingMode(); + MOVI2R(W0, GetCompilerPC()); + MovToPC(W0); + if (off != 0) + ADDI2R(W0, W0, off * 4); + QuickCallFunction(SCRATCH2_64, &JitMemCheck); + + // If 0, the breakpoint wasn't tripped. + CMPI2R(W0, 0); + FixupBranch skip = B(CC_EQ); + WriteDownCount(-1 - off); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(FIELD_NZCV, FLAGTEMPREG); + return true; + } + + return false; +} + void Arm64Jit::Comp_DoNothing(MIPSOpcode op) { } MIPSOpcode Arm64Jit::GetOriginalOp(MIPSOpcode op) { diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index 3d84451384..f504e4cb58 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -214,6 +214,8 @@ private: void WriteExit(u32 destination, int exit_num); void WriteExitDestInR(Arm64Gen::ARM64Reg Reg); void WriteSyscallExit(); + bool CheckJitBreakpoint(u32 addr, int downcountOffset); + bool CheckMemoryBreakpoint(int instructionOffset = 0); // Utility compilation functions void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely); diff --git a/Core/MIPS/x86/JitSafeMem.cpp b/Core/MIPS/x86/JitSafeMem.cpp index 56374ed828..104b6579a0 100644 --- a/Core/MIPS/x86/JitSafeMem.cpp +++ b/Core/MIPS/x86/JitSafeMem.cpp @@ -376,17 +376,10 @@ void JitSafeMem::MemCheckImm(MemoryOpType type) void JitSafeMem::MemCheckAsm(MemoryOpType type) { - const auto memchecks = CBreakPoints::GetMemCheckRanges(); - bool possible = false; + const auto memchecks = CBreakPoints::GetMemCheckRanges(type == MEM_WRITE); + bool possible = !memchecks.empty(); for (auto it = memchecks.begin(), end = memchecks.end(); it != end; ++it) { - if (!(it->cond & MEMCHECK_READ) && type == MEM_READ) - continue; - if (!(it->cond & MEMCHECK_WRITE) && type == MEM_WRITE) - continue; - - possible = true; - FixupBranch skipNext, skipNextRange; if (it->end != 0) { diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 5d187b915f..bf5f9c7995 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -809,7 +809,6 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) if (_bDebug && GetUIState() == UISTATE_INGAME && PSP_IsInited()) { Core_WaitInactive(TEMP_BREAKPOINT_WAIT_MS); - CBreakPoints::ClearTemporaryBreakPoints(); breakpointList->reloadBreakpoints(); threadList->reloadThreads(); stackTraceView->loadStackTrace(); @@ -913,4 +912,4 @@ void CDisasm::UpdateDialog(bool _bComplete) // redraw. all others are updated manually InvalidateRect (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), NULL, TRUE); UpdateWindow (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW)); -} \ No newline at end of file +} From 5510a69d41e77cd0b5f1197f5709d84506bafc3c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 2 May 2018 07:03:23 -0700 Subject: [PATCH 148/301] armjit: Enable breakpoints. --- Core/MIPS/ARM/ArmCompFPU.cpp | 1 + Core/MIPS/ARM/ArmCompLoadStore.cpp | 3 + Core/MIPS/ARM/ArmCompVFPU.cpp | 2 + Core/MIPS/ARM/ArmCompVFPUNEON.cpp | 2 + Core/MIPS/ARM/ArmJit.cpp | 115 ++++++++++++++++++++++++++++- Core/MIPS/ARM/ArmJit.h | 2 + 6 files changed, 121 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompFPU.cpp b/Core/MIPS/ARM/ArmCompFPU.cpp index 971f8cebdc..f4ec293382 100644 --- a/Core/MIPS/ARM/ArmCompFPU.cpp +++ b/Core/MIPS/ARM/ArmCompFPU.cpp @@ -93,6 +93,7 @@ extern int logBlocks; void ArmJit::Comp_FPULS(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); s32 offset = (s16)(op & 0xFFFF); int ft = _FT; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 2c0baac10b..16a3753fd2 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -112,6 +112,7 @@ namespace MIPSComp void ArmJit::Comp_ITypeMemLR(MIPSOpcode op, bool load) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFF); MIPSGPReg rt = _RT; MIPSGPReg rs = _RS; @@ -120,6 +121,7 @@ namespace MIPSComp if (!js.inDelaySlot) { // Optimisation: Combine to single unaligned load/store bool isLeft = (o == 34 || o == 42); + CheckMemoryBreakpoint(1); MIPSOpcode nextOp = GetOffsetInstruction(1); // Find a matching shift in opposite direction with opposite offset. if (nextOp == (isLeft ? (op.encoding + (4<<26) - 3) @@ -259,6 +261,7 @@ namespace MIPSComp void ArmJit::Comp_ITypeMem(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op&0xFFFF); bool load = false; MIPSGPReg rt = _RT; diff --git a/Core/MIPS/ARM/ArmCompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp index 3f275c58be..c171424075 100644 --- a/Core/MIPS/ARM/ArmCompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -226,6 +226,7 @@ namespace MIPSComp void ArmJit::Comp_SV(MIPSOpcode op) { NEON_IF_AVAILABLE(CompNEON_SV); CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); s32 offset = (signed short)(op & 0xFFFC); int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5); @@ -332,6 +333,7 @@ namespace MIPSComp { NEON_IF_AVAILABLE(CompNEON_SVQ); CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int imm = (signed short)(op&0xFFFC); int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5); diff --git a/Core/MIPS/ARM/ArmCompVFPUNEON.cpp b/Core/MIPS/ARM/ArmCompVFPUNEON.cpp index fe49e2e509..9f04436bf4 100644 --- a/Core/MIPS/ARM/ArmCompVFPUNEON.cpp +++ b/Core/MIPS/ARM/ArmCompVFPUNEON.cpp @@ -159,6 +159,7 @@ void ArmJit::CompNEON_VecDo3(MIPSOpcode op) { void ArmJit::CompNEON_SV(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); // Remember to use single lane stores here and not VLDR/VSTR - switching usage // between NEON and VFPU can be expensive on some chips. @@ -276,6 +277,7 @@ inline int MIPS_GET_VQVT(u32 op) { void ArmJit::CompNEON_SVQ(MIPSOpcode op) { CONDITIONAL_DISABLE; + CheckMemoryBreakpoint(); int offset = (signed short)(op & 0xFFFC); int vt = MIPS_GET_VQVT(op.encoding); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index ea206a6d26..87bb801087 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -26,6 +26,7 @@ #include "Core/Config.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" @@ -45,7 +46,7 @@ using namespace ArmJitConstants; -void DisassembleArm(const u8 *data, int size) { +static void DisassembleArm(const u8 *data, int size) { char temp[256]; for (int i = 0; i < size; i += 4) { const u32 *codePtr = (const u32 *)(data + i); @@ -69,6 +70,33 @@ void DisassembleArm(const u8 *data, int size) { } } +static u32 JitBreakpoint() { + // Should we skip this breakpoint? + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc); + if ((result & BREAK_ACTION_PAUSE) == 0) + return 0; + + return 1; +} + +static u32 JitMemCheck(u32 pc) { + if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc) + return 0; + + // Note: pc may be the delay slot. + const auto op = Memory::Read_Instruction(pc, true); + s32 offset = (s16)(op & 0xFFFF); + if (MIPSGetInfo(op) & IS_VFPU) + offset &= 0xFFFC; + u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset; + + CBreakPoints::ExecOpMemCheck(addr, pc); + return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1; +} + namespace MIPSComp { using namespace ArmGen; @@ -86,6 +114,10 @@ ArmJit::ArmJit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(m INFO_LOG(JIT, "ARM JIT initialized: %d MB of code space", GetSpaceLeft() / (1024 * 1024)); js.startDefaultPrefix = mips_->HasDefaultPrefix(); + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } ArmJit::~ArmJit() { @@ -104,6 +136,10 @@ void ArmJit::DoState(PointerWrap &p) } else { js.hasSetRounding = 1; } + + // The debugger sets this so that "go" on a breakpoint will actually... go. + // But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks. + CBreakPoints::SetSkipFirst(0); } void ArmJit::UpdateFCR31() { @@ -158,13 +194,16 @@ void ArmJit::EatInstruction(MIPSOpcode op) { ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot."); } + CheckJitBreakpoint(GetCompilerPC() + 4, 0); js.numInstructions++; js.compilerPC += 4; js.downcountAmount += MIPSGetInstructionCycleEstimate(op); } -void ArmJit::CompileDelaySlot(int flags) -{ +void ArmJit::CompileDelaySlot(int flags) { + // Need to offset the downcount which was already incremented for the branch + delay slot. + CheckJitBreakpoint(GetCompilerPC() + 4, -2); + // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. @@ -295,6 +334,9 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b) while (js.compiling) { gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages + // Jit breakpoints are quite fast, so let's do them in release too. + CheckJitBreakpoint(GetCompilerPC(), 0); + MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC()); //MIPSInfo info = MIPSGetInfo(inst); //if (info & IS_VFPU) { @@ -492,7 +534,18 @@ void ArmJit::Comp_ReplacementFunc(MIPSOpcode op) return; } - if (entry->flags & REPFLAG_DISABLED) { + u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC()); + bool disabled = (entry->flags & REPFLAG_DISABLED) != 0; + if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) { + // We don't need to disable hooks, the code will still run. + if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) { + // Any breakpoint at the func entry was already tripped, so we can still run the replacement. + // That's a common case - just to see how often the replacement hits. + disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32)); + } + } + + if (disabled) { MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this); } else if (entry->jitReplaceFunc) { MIPSReplaceFunc repl = entry->jitReplaceFunc; @@ -686,6 +739,60 @@ void ArmJit::WriteSyscallExit() B((const void *)dispatcherCheckCoreState); } +bool ArmJit::CheckJitBreakpoint(u32 addr, int downcountOffset) { + if (CBreakPoints::IsAddressBreakPoint(addr)) { + MRS(R8); + FlushAll(); + MOVI2R(SCRATCHREG1, GetCompilerPC()); + MovToPC(SCRATCHREG1); + RestoreRoundingMode(); + QuickCallFunction(SCRATCHREG1, &JitBreakpoint); + + // If 0, the conditional breakpoint wasn't taken. + CMPI2R(R0, 0, SCRATCHREG2); + FixupBranch skip = B_CC(CC_EQ); + WriteDownCount(downcountOffset); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(true, false, R8); + return true; + } + + return false; +} + +bool ArmJit::CheckMemoryBreakpoint(int instructionOffset) { + if (CBreakPoints::HasMemChecks()) { + int off = instructionOffset + (js.inDelaySlot ? 1 : 0); + + MRS(R8); + FlushAll(); + RestoreRoundingMode(); + MOVI2R(R0, GetCompilerPC()); + MovToPC(R0); + if (off != 0) + ADDI2R(R0, R0, off, SCRATCHREG2); + QuickCallFunction(SCRATCHREG2, &JitMemCheck); + + // If 0, the breakpoint wasn't tripped. + CMPI2R(R0, 0, SCRATCHREG2); + FixupBranch skip = B_CC(CC_EQ); + WriteDownCount(-1 - off); + ApplyRoundingMode(); + B((const void *)dispatcherCheckCoreState); + SetJumpTarget(skip); + + ApplyRoundingMode(); + _MSR(true, false, R8); + return true; + } + + return false; +} + void ArmJit::Comp_DoNothing(MIPSOpcode op) { } MIPSOpcode ArmJit::GetOriginalOp(MIPSOpcode op) { diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index af2b6a128d..07b4b0f5a6 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -215,6 +215,8 @@ private: void WriteExit(u32 destination, int exit_num); void WriteExitDestInR(ArmGen::ARMReg Reg); void WriteSyscallExit(); + bool CheckJitBreakpoint(u32 addr, int downcountOffset); + bool CheckMemoryBreakpoint(int instructionOffset = 0); // Utility compilation functions void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely); From 4c3fe4737209abfc7763dfa6b63fdf19339c4e17 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 2 May 2018 07:07:03 -0700 Subject: [PATCH 149/301] jit: Remove unused breakpoint code. --- Core/MIPS/ARM/ArmAsm.cpp | 1 - Core/MIPS/ARM/ArmJit.h | 2 -- Core/MIPS/ARM64/Arm64Asm.cpp | 1 - Core/MIPS/ARM64/Arm64Jit.h | 2 -- Core/MIPS/MIPS/MipsJit.h | 2 -- Core/MIPS/x86/Asm.cpp | 5 ----- Core/MIPS/x86/Jit.h | 2 -- 7 files changed, 15 deletions(-) diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 7e037a7052..3a5bcba1a5 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -238,7 +238,6 @@ void ArmJit::GenerateFixedCode() { B_CC(CC_EQ, outerLoop); SetJumpTarget(badCoreState); - breakpointBailout = GetCodePtr(); SaveDowncount(); RestoreRoundingMode(true); diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 07b4b0f5a6..80a3bc6ae9 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -313,8 +313,6 @@ public: const u8 *restoreRoundingMode; const u8 *applyRoundingMode; - - const u8 *breakpointBailout; }; } // namespace MIPSComp diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 2390ed9673..1abf148d7c 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -275,7 +275,6 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { B(CC_EQ, outerLoop); SetJumpTarget(badCoreState); - breakpointBailout = GetCodePtr(); SaveStaticRegisters(); RestoreRoundingMode(true); diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index f504e4cb58..6ad5921d53 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -274,8 +274,6 @@ public: const u8 *dispatcher; const u8 *dispatcherNoCheck; - const u8 *breakpointBailout; - const u8 *saveStaticRegisters; const u8 *loadStaticRegisters; diff --git a/Core/MIPS/MIPS/MipsJit.h b/Core/MIPS/MIPS/MipsJit.h index 88c1c2dc6d..23b164b1e7 100644 --- a/Core/MIPS/MIPS/MipsJit.h +++ b/Core/MIPS/MIPS/MipsJit.h @@ -181,8 +181,6 @@ public: const u8 *dispatcherPCInR0; const u8 *dispatcher; const u8 *dispatcherNoCheck; - - const u8 *breakpointBailout; }; typedef void (MipsJit::*MIPSCompileFunc)(MIPSOpcode opcode); diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index a6242801ff..fd1815132d 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -211,11 +211,6 @@ void Jit::GenerateFixedCode(JitOptions &jo) { ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); - breakpointBailout = GetCodePtr(); - RestoreRoundingMode(true); - ABI_PopAllCalleeSavedRegsAndAdjustStack(); - RET(); - // Let's spare the pre-generated code from unprotect-reprotect. endOfPregeneratedCode = AlignCodePage(); EndWrite(); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 1c397005ff..0eefe89c40 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -320,8 +320,6 @@ private: const u8 *dispatcherNoCheck; const u8 *dispatcherInEAXNoCheck; - const u8 *breakpointBailout; - const u8 *restoreRoundingMode; const u8 *applyRoundingMode; From 65d072c7b06342d665ead68e6e321f0d7d8c4d00 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 11 Apr 2018 21:33:52 -0700 Subject: [PATCH 150/301] http: Add some operations to avoid blocking. --- ext/native/net/sinks.cpp | 9 +++++++-- ext/native/net/sinks.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ext/native/net/sinks.cpp b/ext/native/net/sinks.cpp index aaac2f1888..d8d47dd080 100644 --- a/ext/native/net/sinks.cpp +++ b/ext/native/net/sinks.cpp @@ -201,6 +201,11 @@ bool InputSink::Empty() { return valid_ == 0; } +bool InputSink::TryFill() { + Fill(); + return !Empty(); +} + OutputSink::OutputSink(size_t fd) : fd_(fd), read_(0), write_(0), valid_(0) { fd_util::SetNonBlocking((int)fd_, true); } @@ -316,7 +321,7 @@ bool OutputSink::Block() { return true; } -bool OutputSink::Flush() { +bool OutputSink::Flush(bool allowBlock) { while (valid_ > 0) { size_t avail = std::min(BUFFER_SIZE - read_, valid_); @@ -325,7 +330,7 @@ bool OutputSink::Flush() { if (bytes == 0) { // This may also drain. Either way, keep looping. - if (!Block()) { + if (!allowBlock || !Block()) { return false; } } diff --git a/ext/native/net/sinks.h b/ext/native/net/sinks.h index 0138d74a6c..308c784fb0 100644 --- a/ext/native/net/sinks.h +++ b/ext/native/net/sinks.h @@ -21,6 +21,7 @@ public: bool Skip(size_t bytes); bool Empty(); + bool TryFill(); private: void Fill(); @@ -49,7 +50,7 @@ public: bool PushCRLF(const std::string &s); bool Printf(const char *fmt, ...); - bool Flush(); + bool Flush(bool allowBlock = true); bool Empty(); From b75b6801c80f31e0901d9b10f8f3916ddba032d1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 11 Apr 2018 22:40:46 -0700 Subject: [PATCH 151/301] http: Add a simple WebSocket server class. --- CMakeLists.txt | 4 + ext/native/Android.mk | 2 + ext/native/data/base64.cpp | 35 +++ ext/native/data/base64.h | 5 + ext/native/native.vcxproj | 4 + ext/native/native.vcxproj.filters | 12 + ext/native/net/http_server.cpp | 6 +- ext/native/net/websocket_server.cpp | 449 ++++++++++++++++++++++++++++ ext/native/net/websocket_server.h | 95 ++++++ 9 files changed, 610 insertions(+), 2 deletions(-) create mode 100644 ext/native/data/base64.cpp create mode 100644 ext/native/data/base64.h create mode 100644 ext/native/net/websocket_server.cpp create mode 100644 ext/native/net/websocket_server.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 97129aba3a..ea354847c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -893,6 +893,8 @@ add_library(native STATIC ext/native/base/stringutil.h ext/native/base/timeutil.cpp ext/native/base/timeutil.h + ext/native/data/base64.cpp + ext/native/data/base64.h ext/native/data/compression.cpp ext/native/data/compression.h ext/native/file/chunk_file.cpp @@ -985,6 +987,8 @@ add_library(native STATIC ext/native/net/sinks.h ext/native/net/url.cpp ext/native/net/url.h + ext/native/net/websocket_server.cpp + ext/native/net/websocket_server.h ext/native/profiler/profiler.cpp ext/native/profiler/profiler.h ext/native/thin3d/thin3d.cpp diff --git a/ext/native/Android.mk b/ext/native/Android.mk index bcef6cec25..b878bf1463 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -13,6 +13,7 @@ LOCAL_SRC_FILES :=\ base/timeutil.cpp \ base/colorutil.cpp \ base/stringutil.cpp \ + data/base64.cpp \ data/compression.cpp \ ext/rg_etc1/rg_etc1.cpp \ ext/cityhash/city.cpp \ @@ -64,6 +65,7 @@ LOCAL_SRC_FILES :=\ net/resolve.cpp \ net/sinks.cpp \ net/url.cpp \ + net/websocket_server.cpp \ profiler/profiler.cpp \ thread/executor.cpp \ thread/threadutil.cpp \ diff --git a/ext/native/data/base64.cpp b/ext/native/data/base64.cpp new file mode 100644 index 0000000000..82d428468c --- /dev/null +++ b/ext/native/data/base64.cpp @@ -0,0 +1,35 @@ +#include "data/base64.h" + +// TODO: This is a simple but not very efficient implementation. +std::string Base64Encode(const uint8_t *p, size_t sz) { + const char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + size_t unpaddedLength = (4 * sz + 3) / 3; + std::string result; + result.resize((unpaddedLength + 3) & ~3, '='); + + for (size_t i = 0; i < unpaddedLength; ++i) { + // This is the index into the original string. + size_t pos = (i * 3) / 4; + int8_t off = 2 * ((i * 3) % 4); + + int c = p[pos]; + if (off > 2) { + c <<= 8; + off -= 8; + + // Grab more bits from the next character. + if (pos + 1 < sz) { + c |= p[pos + 1]; + } + } + + // Since we take from the big end, off starts at 2 and goes down. + int8_t shift = 2 - off; + + // Now take the bits at off and encode the character. + result[i] = digits[(c >> shift) & 0x3F]; + } + + return result; +} diff --git a/ext/native/data/base64.h b/ext/native/data/base64.h new file mode 100644 index 0000000000..b963bfb391 --- /dev/null +++ b/ext/native/data/base64.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string Base64Encode(const uint8_t *p, size_t sz); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 556c328d4f..8fb5a42746 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -202,6 +202,7 @@ + @@ -238,6 +239,7 @@ + @@ -317,6 +319,7 @@ + AnySuitable @@ -700,6 +703,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index 999519c3bd..08305fee7c 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -341,6 +341,12 @@ json + + net + + + data + @@ -814,6 +820,12 @@ json + + net + + + data + diff --git a/ext/native/net/http_server.cpp b/ext/native/net/http_server.cpp index ddd893e5ac..ace77907c4 100644 --- a/ext/native/net/http_server.cpp +++ b/ext/native/net/http_server.cpp @@ -87,8 +87,10 @@ void Request::WriteHttpResponseHeader(int status, int64_t size, const char *mime net::OutputSink *buffer = Out(); buffer->Printf("HTTP/1.0 %03d %s\r\n", status, statusStr); buffer->Push("Server: PPSSPPServer v0.1\r\n"); - buffer->Printf("Content-Type: %s\r\n", mimeType ? mimeType : DEFAULT_MIME_TYPE); - buffer->Push("Connection: close\r\n"); + if (!mimeType || strcmp(mimeType, "websocket") != 0) { + buffer->Printf("Content-Type: %s\r\n", mimeType ? mimeType : DEFAULT_MIME_TYPE); + buffer->Push("Connection: close\r\n"); + } if (size >= 0) { buffer->Printf("Content-Length: %llu\r\n", size); } diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp new file mode 100644 index 0000000000..05b5279387 --- /dev/null +++ b/ext/native/net/websocket_server.cpp @@ -0,0 +1,449 @@ +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#endif +#include "base/stringutil.h" +#include "data/base64.h" +#include "net/http_server.h" +#include "net/sinks.h" +#include "net/websocket_server.h" +// TODO: Not a great cross dependency. +#include "Common/Crypto/sha1.h" + +#ifdef _WIN32 +// Function Cross-Compatibility +#define strcasecmp _stricmp +#endif + +static const char *const WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +namespace net { + +enum class Opcode { + CONTINUE = 0, + TEXT = 1, + BINARY = 2, + + CLOSE = 8, + PING = 9, + PONG = 10, + + PAYLOAD_MAX = 2, + CONTROL_MIN = 8, + CONTROL_MAX = 10, +}; + +WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request) { + auto requireHeader = [&](const char *name, const char *expected) { + std::string val; + if (!request.GetHeader(name, &val)) { + return false; + } + return strcasecmp(val.c_str(), expected) == 0; + }; + auto requireHeaderContains = [&](const char *name, const char *expected) { + std::string val; + if (!request.GetHeader(name, &val)) { + return false; + } + std::transform(val.begin(), val.end(), val.begin(), tolower); + return strstr(val.c_str(), expected) != 0; + }; + + if (!requireHeader("upgrade", "websocket") || !requireHeaderContains("connection", "upgrade")) { + request.WriteHttpResponseHeader(400, -1, "text/plain"); + request.Out()->Push("Must send a websocket request."); + return nullptr; + } + if (!requireHeader("sec-websocket-version", "13")) { + request.WriteHttpResponseHeader(400, -1, "text/plain", "Sec-WebSocket-Version: 13\r\n"); + request.Out()->Push("Unsupported version."); + return nullptr; + } + + std::string key; + if (!request.GetHeader("sec-websocket-key", &key)) { + request.WriteHttpResponseHeader(400, -1, "text/plain"); + request.Out()->Push("Cannot accept without key."); + return nullptr; + } + + key += WEBSOCKET_GUID; + unsigned char accept[20]; + sha1((unsigned char *)key.c_str(), (int)key.size(), accept); + + std::string acceptKey = Base64Encode(accept, 20); + std::string otherHeaders = StringFromFormat("Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n", acceptKey.c_str()); + + // Okay, we're good to go then. + request.WriteHttpResponseHeader(101, -1, "websocket", otherHeaders.c_str()); + request.WritePartial(); + + return new WebSocketServer(request.fd(), request.In(), request.Out()); +} + +void WebSocketServer::Send(const std::string &str) { + assert(open_); + SendHeader(true, (int)Opcode::TEXT, str.size()); + // TODO: For long strings, this will block. Possibly not ideal. + if (!out_->Push(str.c_str(), str.size())) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } +} + +void WebSocketServer::Send(const std::vector &payload) { + assert(open_); + SendHeader(true, (int)Opcode::BINARY, payload.size()); + // TODO: For long strings, this will block. Possibly not ideal. + if (!out_->Push((const char *)&payload[0], payload.size())) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } +} + +void WebSocketServer::Ping(const std::vector &payload) { + assert(open_); + assert(payload.size() <= 125); + SendHeader(true, (int)Opcode::PING, payload.size()); + if (!out_->Push((const char *)&payload[0], payload.size())) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } +} + +void WebSocketServer::Pong(const std::vector &payload) { + assert(open_); + assert(payload.size() <= 125); + SendHeader(true, (int)Opcode::PONG, payload.size()); + if (!out_->Push((const char *)&payload[0], payload.size())) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } +} + +void WebSocketServer::Close(WebSocketClose reason) { + closeReason_ = reason; + SendHeader(true, (int)Opcode::CLOSE, 2); + + uint16_t r = (uint16_t)reason; + uint8_t reasonData[] = { + (uint8_t)((r >> 8) & 0xFF), + (uint8_t)((r >> 0) & 0xFF), + }; + if (!out_->Push((const char *)reasonData, sizeof(reasonData))) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } + + sentClose_ = true; +} + +bool WebSocketServer::Process(float timeout) { + if (!open_) { + return false; + } + + out_->Flush(false); + + if (out_->Empty() && sentClose_) { + // Okay, we've sent the close. Don't wait for anything else (whether we got a close or not.) + open_ = false; + return false; + } + + struct timeval tv; + tv.tv_sec = floor(timeout); + tv.tv_usec = (timeout - floor(timeout)) * 1000000.0; + + fd_set read; + FD_ZERO(&read); + // In case we closed due to protocol error, don't even try to read. + if (!sentClose_) { + FD_SET(fd_, &read); + } + + fd_set write; + FD_ZERO(&write); + if (!out_->Empty()) { + FD_SET(fd_, &write); + } + + // First argument to select is the highest socket in the set + 1. + int rval = select((int)fd_ + 1, &read, &write, nullptr, &tv); + if (rval < 0) { + // Something went wrong with the select() call. + // TODO: Could be EINTR, for now returning true... + return true; + } + + if (rval == 0) { + // Timed out. + return true; + } + + if (FD_ISSET(fd_, &write)) { + out_->Flush(false); + } + if (FD_ISSET(fd_, &read)) { + if (in_->Empty() && !in_->TryFill()) { + // Since select said it was readable, we assume this means disconnect. + closeReason_ = WebSocketClose::ABNORMAL; + open_ = false; + return false; + } + + while (ReadFrames() && !in_->Empty()) + continue; + } + + return true; +} + +bool WebSocketServer::ReadFrames() { + if (pendingLeft_ != 0) { + return ReadPending(); + } + + return ReadFrame(); +} + +bool WebSocketServer::ReadFrame() { + assert(pendingLeft_ == 0); + + // TODO: For now blocking on header trickle, shouldn't be common. + auto readExact = [&](void *p, size_t sz) { + if (!in_->TakeExact((char *)p, sz)) { + // TODO: Failing on too slow trickle timeout for now. + Close(WebSocketClose::POLICY_VIOLATION); + return false; + } + return true; + }; + + // Client frames are always between 6 and 14 bytes. We start with 6. + uint8_t header[14]; + if (!readExact(header, 6)) + return false; + + // Don't allow reserved bits to be set, require masking. + if ((header[0] & 0x70) != 0 || (header[1] & 0x80) == 0) { + Close(WebSocketClose::PROTOCOL_ERROR); + return false; + } + + const bool fin = (header[0] & 0x80) != 0; + const int opcode = header[0] & 0x0F; + uint64_t sz = header[1] & 0x7F; + const uint8_t *mask = &header[2]; + + if (opcode >= (int)Opcode::CONTROL_MIN && (sz > 125 || !fin)) { + // Control frames must be <= 125 bytes. + Close(WebSocketClose::PROTOCOL_ERROR); + return false; + } + + if (opcode > (int)Opcode::CONTROL_MAX || (opcode > (int)Opcode::PAYLOAD_MAX && opcode < (int)Opcode::CONTROL_MIN)) { + // Undefined opcode. + Close(WebSocketClose::PROTOCOL_ERROR); + return false; + } + + if (!pendingFin_ && opcode == (int)Opcode::CONTINUE) { + // Can't continue what you haven't started. + Close(WebSocketClose::PROTOCOL_ERROR); + return false; + } + if (pendingFin_ && opcode != (int)Opcode::CONTINUE && opcode < (int)Opcode::CONTROL_MIN) { + // Can't start something else until you finish your thought. + Close(WebSocketClose::PROTOCOL_ERROR); + return false; + } + + if (sz == 126) { + // Read the rest of the mask. + if (!readExact((char *)&header[6], 2)) + return false; + + mask = &header[4]; + sz = (header[2] << 8) | (header[3] << 0); + } else if (sz == 127) { + // We only have half the size so far - read the rest, and the mask. + if (!readExact((char *)&header[6], 8)) + return false; + + mask = &header[10]; + // Read from big endian. + uint64_t high = (header[2] << 24) | (header[3] << 16) || (header[4] << 8) | (header[5] << 0); + uint64_t low = (header[6] << 24) | (header[7] << 16) || (header[8] << 8) | (header[9] << 0); + sz = (high << 32) | low; + + if ((sz & 0x8000000000000000ULL) != 0) { + Close(WebSocketClose::PROTOCOL_ERROR); + return false; + } + } + + if (opcode >= (int)Opcode::CONTROL_MIN) { + // It's safe to overwrite this since we can be between fragmented frames, but not inside a frame. + memcpy(pendingMask_, mask, sizeof(pendingMask_)); + return ReadControlFrame(opcode, sz); + } + + // The data could be split among many TCP packets, so read it as it comes. + if (!pendingFin_) + pendingOpcode_ = opcode; + pendingFin_ = !fin; + pendingLeft_ = sz; + memcpy(pendingMask_, mask, sizeof(pendingMask_)); + + // Payload data is actually read in ReadPending(). + return true; +} + +bool WebSocketServer::ReadPending() { + size_t pos = pendingBuf_.size(); + pendingBuf_.resize(pendingBuf_.size() + pendingLeft_); + + // Read what we can. + size_t readBytes = in_->TakeAtMost((char *)&pendingBuf_[pos], pendingLeft_); + for (size_t i = 0; i < readBytes; ++i) { + pendingBuf_[pos + i] ^= pendingMask_[i & 3]; + } + pendingLeft_ -= readBytes; + + if (pendingLeft_ != 0) { + // Still more to read. Careful: we might need to rotate the mask. + // Example: if we read only 3 bytes, next read should start at fourth byte in mask. + int offset = readBytes & 3; + if (offset) { + uint8_t orig[4]; + memcpy(orig, pendingMask_, sizeof(orig)); + for (size_t i = 0; i < sizeof(orig); ++i) { + pendingMask_[i] = orig[(offset + i) & 3]; + } + } + + // Truncate out the unread bytes for next time. + pendingBuf_.resize(pos + readBytes); + return true; + } + + // We're done, but were we waiting for a FIN packet? + if (pendingFin_) + return true; + + if (pendingOpcode_ == (int)Opcode::TEXT) { + if (text_) { + text_(std::string(pendingBuf_.begin(), pendingBuf_.end())); + } + } else if (pendingOpcode_ == (int)Opcode::BINARY) { + if (binary_) { + binary_(pendingBuf_); + } + } else { + assert(false); + } + + // All done, clear it out. + pendingBuf_.clear(); + pendingOpcode_ = -1; + + return true; +} + +bool WebSocketServer::ReadControlFrame(int opcode, size_t sz) { + std::vector payload; + payload.resize(sz); + // Just block here to read the payload. + if (!in_->TakeExact((char *)&payload[0], sz)) { + // TODO: Failing on too slow trickle timeout for now. + Close(WebSocketClose::POLICY_VIOLATION); + return false; + } + + for (size_t i = 0; i < sz; ++i) { + payload[i] ^= pendingMask_[i & 3]; + } + + if (opcode == (int)Opcode::PING) { + Pong(payload); + // Try to send immediately if possible, but don't block. + out_->Flush(false); + + if (ping_) { + ping_(payload); + } + } else if (opcode == (int)Opcode::PONG) { + if (pong_) { + pong_(payload); + } + } else if (opcode == (int)Opcode::CLOSE) { + if (payload.size() >= 2) { + uint16_t reason = (payload[0] << 8) | payload[1]; + // Send back a close right away. + Close(WebSocketClose(reason)); + } else { + Close(WebSocketClose::NO_STATUS); + } + // Don't read anything more. + return false; + } else { + assert(false); + } + + return true; +} + +void WebSocketServer::SendHeader(bool fin, int opcode, size_t sz) { + assert((opcode & 0x0F) == opcode); + uint8_t frameHeader = (fin ? 0x80 : 0x00) | opcode; + out_->Push((const char *)&frameHeader, 1); + + // We never mask from the server. + if (sz <= 125) { + uint8_t frameSize = (int8_t)sz; + out_->Push((const char *)&frameSize, 1); + } else if (sz <= 0xFFFF) { + uint8_t frameSize[] = { + 126, + (uint8_t)((sz >> 8) & 0xFF), + (uint8_t)((sz >> 0) & 0xFF), + }; + out_->Push((const char *)frameSize, sizeof(frameSize)); + } else { + uint64_t sz64 = sz; + assert((sz64 & 0x8000000000000000ULL) == 0); + uint8_t frameSize[] = { + 127, + (uint8_t)((sz64 >> 56) & 0xFF), + (uint8_t)((sz64 >> 48) & 0xFF), + (uint8_t)((sz64 >> 40) & 0xFF), + (uint8_t)((sz64 >> 32) & 0xFF), + (uint8_t)((sz64 >> 24) & 0xFF), + (uint8_t)((sz64 >> 16) & 0xFF), + (uint8_t)((sz64 >> 8) & 0xFF), + (uint8_t)((sz64 >> 0) & 0xFF), + }; + out_->Push((const char *)frameSize, sizeof(frameSize)); + } +} + +}; diff --git a/ext/native/net/websocket_server.h b/ext/native/net/websocket_server.h new file mode 100644 index 0000000000..2aed3254f7 --- /dev/null +++ b/ext/native/net/websocket_server.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include "net/http_server.h" +#include "net/sinks.h" + +namespace net { + +enum class WebSocketClose : uint16_t { + NORMAL = 1000, + GOING_AWAY = 1001, + PROTOCOL_ERROR = 1002, + UNSUPPORTED_DATA = 1003, + INVALID_DATA = 1007, + POLICY_VIOLATION = 1008, + MESSAGE_TOO_LONG = 1009, + MISSING_EXTENSION = 1010, + INTERNAL_ERROR = 1011, + SERVICE_RESTART = 1012, + TRY_AGAIN_LATER = 1013, + BAD_GATEWAY = 1014, + + NO_STATUS = 1005, + ABNORMAL = 1006, +}; + +// RFC 6455 +class WebSocketServer { +public: + static WebSocketServer *CreateAsUpgrade(const http::Request &request); + + // TODO: Doesn't support fragmented data yet. + void Send(const std::string &str); + void Send(const std::vector &payload); + void Ping(const std::vector &payload = {}); + void Pong(const std::vector &payload = {}); + void Close(WebSocketClose reason = WebSocketClose::GOING_AWAY); + + // Note: may interrupt early. Call in a loop. + bool Process(float timeout = -1.0f); + + void SetTextHandler(std::function func) { + text_ = func; + } + void SetBinaryHandler(std::function &)> func) { + binary_ = func; + } + // Doesn't need to send a Pong. + void SetPingHandler(std::function &)> func) { + ping_ = func; + } + void SetPongHandler(std::function &)> func) { + pong_ = func; + } + + bool IsOpen() { + return open_; + } + WebSocketClose CloseReason() { + return closeReason_; + } + +protected: + WebSocketServer(size_t fd, InputSink *in, OutputSink *out) : fd_(fd), in_(in), out_(out) { + } + + void SendHeader(bool fin, int opcode, size_t sz); + bool ReadFrames(); + bool ReadFrame(); + bool ReadPending(); + bool ReadControlFrame(int opcode, size_t sz); + + bool open_ = true; + bool sentClose_ = false; + size_t fd_ = 0; + InputSink *in_ = nullptr; + OutputSink *out_ = nullptr; + WebSocketClose closeReason_ = WebSocketClose::NO_STATUS; + + std::vector pendingBuf_; + uint8_t pendingMask_[4]{}; + // Bytes left to read in the frame (in case of a partial frame read.) + uint64_t pendingLeft_ = 0; + int pendingOpcode_ = -1; + // Waiting for a frame with FIN. + bool pendingFin_ = false; + + std::function text_; + std::function &)> binary_; + std::function &)> ping_; + std::function &)> pong_; +}; + +}; From 8b094f8c6f992f5a044b005f2f1a7797db2bb33b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 11 Apr 2018 22:53:41 -0700 Subject: [PATCH 152/301] http: Allow fragmented data for WebSocket. --- ext/native/net/websocket_server.cpp | 44 +++++++++++++++++++++++++++++ ext/native/net/websocket_server.h | 8 +++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index 05b5279387..e71a6050f0 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -96,6 +96,7 @@ WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request) void WebSocketServer::Send(const std::string &str) { assert(open_); + assert(fragmentOpcode_ == -1); SendHeader(true, (int)Opcode::TEXT, str.size()); // TODO: For long strings, this will block. Possibly not ideal. if (!out_->Push(str.c_str(), str.size())) { @@ -107,6 +108,7 @@ void WebSocketServer::Send(const std::string &str) { void WebSocketServer::Send(const std::vector &payload) { assert(open_); + assert(fragmentOpcode_ == -1); SendHeader(true, (int)Opcode::BINARY, payload.size()); // TODO: For long strings, this will block. Possibly not ideal. if (!out_->Push((const char *)&payload[0], payload.size())) { @@ -116,6 +118,48 @@ void WebSocketServer::Send(const std::vector &payload) { } } +void WebSocketServer::AddFragment(bool finish, const std::string &str) { + assert(open_); + if (fragmentOpcode_ == -1) { + SendHeader(finish, (int)Opcode::TEXT, str.size()); + fragmentOpcode_ = (int)Opcode::TEXT; + } else if (fragmentOpcode_ == (int)Opcode::TEXT) { + SendHeader(finish, (int)Opcode::CONTINUE, str.size()); + } else { + assert(fragmentOpcode_ == (int)Opcode::TEXT || fragmentOpcode_ == -1); + } + // TODO: For long strings, this will block. Possibly not ideal. + if (!out_->Push(str.c_str(), str.size())) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } + if (finish) { + fragmentOpcode_ = -1; + } +} + +void WebSocketServer::AddFragment(bool finish, const std::vector &payload) { + assert(open_); + if (fragmentOpcode_ == -1) { + SendHeader(finish, (int)Opcode::BINARY, payload.size()); + fragmentOpcode_ = (int)Opcode::BINARY; + } else if (fragmentOpcode_ == (int)Opcode::BINARY) { + SendHeader(finish, (int)Opcode::CONTINUE, payload.size()); + } else { + assert(fragmentOpcode_ == (int)Opcode::BINARY || fragmentOpcode_ == -1); + } + // TODO: For long strings, this will block. Possibly not ideal. + if (!out_->Push((const char *)&payload[0], payload.size())) { + assert(false); + open_ = false; + closeReason_ = WebSocketClose::ABNORMAL; + } + if (finish) { + fragmentOpcode_ = -1; + } +} + void WebSocketServer::Ping(const std::vector &payload) { assert(open_); assert(payload.size() <= 125); diff --git a/ext/native/net/websocket_server.h b/ext/native/net/websocket_server.h index 2aed3254f7..dae09d3d68 100644 --- a/ext/native/net/websocket_server.h +++ b/ext/native/net/websocket_server.h @@ -30,9 +30,14 @@ class WebSocketServer { public: static WebSocketServer *CreateAsUpgrade(const http::Request &request); - // TODO: Doesn't support fragmented data yet. void Send(const std::string &str); void Send(const std::vector &payload); + + // Call with finish = false to start and continue, then finally with finish = true to complete. + // Note: Fragmented data cannot be interleaved, per protocol. + void AddFragment(bool finish, const std::string &str); + void AddFragment(bool finish, const std::vector &payload); + void Ping(const std::vector &payload = {}); void Pong(const std::vector &payload = {}); void Close(WebSocketClose reason = WebSocketClose::GOING_AWAY); @@ -73,6 +78,7 @@ protected: bool open_ = true; bool sentClose_ = false; + int fragmentOpcode_ = -1; size_t fd_ = 0; InputSink *in_ = nullptr; OutputSink *out_ = nullptr; From 556a46f9d5250dc1d26a612f35d40db3bb0b7727 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 Apr 2018 00:08:45 -0700 Subject: [PATCH 153/301] http: Allow selecting a WebSocket subprotocol. --- ext/native/net/websocket_server.cpp | 40 ++++++++++++++++++++++++++--- ext/native/net/websocket_server.h | 2 +- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index e71a6050f0..ce9af81fc0 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -45,7 +45,32 @@ enum class Opcode { CONTROL_MAX = 10, }; -WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request) { +static inline std::string TrimString(const std::string &s) { + auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { + // isspace() expects 0 - 255, so convert any sign-extended value. + return std::isspace(c & 0xFF); + }); + auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){ + return std::isspace(c & 0xFF); + }).base(); + return wsback > wsfront ? std::string(wsfront, wsback) : std::string(); +} + +static bool ListContainsNoCase(const std::string &list, const std::string value) { + std::vector split; + SplitString(list, ',', split); + + for (auto item : split) { + std::transform(item.begin(), item.end(), item.begin(), tolower); + if (TrimString(item) == value) { + return true; + } + } + + return false; +} + +WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request, const std::string &protocol) { auto requireHeader = [&](const char *name, const char *expected) { std::string val; if (!request.GetHeader(name, &val)) { @@ -58,8 +83,7 @@ WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request) if (!request.GetHeader(name, &val)) { return false; } - std::transform(val.begin(), val.end(), val.begin(), tolower); - return strstr(val.c_str(), expected) != 0; + return ListContainsNoCase(val, expected); }; if (!requireHeader("upgrade", "websocket") || !requireHeaderContains("connection", "upgrade")) { @@ -73,6 +97,14 @@ WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request) return nullptr; } + std::string requestedProtocols; + std::string obtainedProtocolHeader; + if (!protocol.empty() && request.GetHeader("sec-websocket-protocol", &requestedProtocols)) { + if (ListContainsNoCase(requestedProtocols, protocol)) { + obtainedProtocolHeader = "Sec-WebSocket-Protocol: " + protocol + "\r\n"; + } + } + std::string key; if (!request.GetHeader("sec-websocket-key", &key)) { request.WriteHttpResponseHeader(400, -1, "text/plain"); @@ -85,7 +117,7 @@ WebSocketServer *WebSocketServer::CreateAsUpgrade(const http::Request &request) sha1((unsigned char *)key.c_str(), (int)key.size(), accept); std::string acceptKey = Base64Encode(accept, 20); - std::string otherHeaders = StringFromFormat("Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n", acceptKey.c_str()); + std::string otherHeaders = StringFromFormat("Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n%s", acceptKey.c_str(), obtainedProtocolHeader.c_str()); // Okay, we're good to go then. request.WriteHttpResponseHeader(101, -1, "websocket", otherHeaders.c_str()); diff --git a/ext/native/net/websocket_server.h b/ext/native/net/websocket_server.h index dae09d3d68..edd753fed6 100644 --- a/ext/native/net/websocket_server.h +++ b/ext/native/net/websocket_server.h @@ -28,7 +28,7 @@ enum class WebSocketClose : uint16_t { // RFC 6455 class WebSocketServer { public: - static WebSocketServer *CreateAsUpgrade(const http::Request &request); + static WebSocketServer *CreateAsUpgrade(const http::Request &request, const std::string &protocol = ""); void Send(const std::string &str); void Send(const std::vector &payload); From 0fb15fc0d089ab8288fdd43fe489fd60f7922f71 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 Apr 2018 07:56:53 -0700 Subject: [PATCH 154/301] http: Use a buffer for WebSocket output. This way we can more safely write as much as we want. --- ext/native/net/websocket_server.cpp | 99 +++++++++++++++-------------- ext/native/net/websocket_server.h | 3 + 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index ce9af81fc0..2c7ec1cd54 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -130,24 +130,14 @@ void WebSocketServer::Send(const std::string &str) { assert(open_); assert(fragmentOpcode_ == -1); SendHeader(true, (int)Opcode::TEXT, str.size()); - // TODO: For long strings, this will block. Possibly not ideal. - if (!out_->Push(str.c_str(), str.size())) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes(str.c_str(), str.size()); } void WebSocketServer::Send(const std::vector &payload) { assert(open_); assert(fragmentOpcode_ == -1); SendHeader(true, (int)Opcode::BINARY, payload.size()); - // TODO: For long strings, this will block. Possibly not ideal. - if (!out_->Push((const char *)&payload[0], payload.size())) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes((const char *)&payload[0], payload.size()); } void WebSocketServer::AddFragment(bool finish, const std::string &str) { @@ -160,12 +150,7 @@ void WebSocketServer::AddFragment(bool finish, const std::string &str) { } else { assert(fragmentOpcode_ == (int)Opcode::TEXT || fragmentOpcode_ == -1); } - // TODO: For long strings, this will block. Possibly not ideal. - if (!out_->Push(str.c_str(), str.size())) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes(str.c_str(), str.size()); if (finish) { fragmentOpcode_ = -1; } @@ -181,12 +166,7 @@ void WebSocketServer::AddFragment(bool finish, const std::vector &paylo } else { assert(fragmentOpcode_ == (int)Opcode::BINARY || fragmentOpcode_ == -1); } - // TODO: For long strings, this will block. Possibly not ideal. - if (!out_->Push((const char *)&payload[0], payload.size())) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes((const char *)&payload[0], payload.size()); if (finish) { fragmentOpcode_ = -1; } @@ -196,22 +176,14 @@ void WebSocketServer::Ping(const std::vector &payload) { assert(open_); assert(payload.size() <= 125); SendHeader(true, (int)Opcode::PING, payload.size()); - if (!out_->Push((const char *)&payload[0], payload.size())) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes((const char *)&payload[0], payload.size()); } void WebSocketServer::Pong(const std::vector &payload) { assert(open_); assert(payload.size() <= 125); SendHeader(true, (int)Opcode::PONG, payload.size()); - if (!out_->Push((const char *)&payload[0], payload.size())) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes((const char *)&payload[0], payload.size()); } void WebSocketServer::Close(WebSocketClose reason) { @@ -223,11 +195,7 @@ void WebSocketServer::Close(WebSocketClose reason) { (uint8_t)((r >> 8) & 0xFF), (uint8_t)((r >> 0) & 0xFF), }; - if (!out_->Push((const char *)reasonData, sizeof(reasonData))) { - assert(false); - open_ = false; - closeReason_ = WebSocketClose::ABNORMAL; - } + SendBytes((const char *)reasonData, sizeof(reasonData)); sentClose_ = true; } @@ -237,9 +205,9 @@ bool WebSocketServer::Process(float timeout) { return false; } - out_->Flush(false); + SendFlush(); - if (out_->Empty() && sentClose_) { + if (outBuf_.empty() && out_->Empty() && sentClose_) { // Okay, we've sent the close. Don't wait for anything else (whether we got a close or not.) open_ = false; return false; @@ -258,7 +226,7 @@ bool WebSocketServer::Process(float timeout) { fd_set write; FD_ZERO(&write); - if (!out_->Empty()) { + if (!outBuf_.empty() || !out_->Empty()) { FD_SET(fd_, &write); } @@ -276,7 +244,7 @@ bool WebSocketServer::Process(float timeout) { } if (FD_ISSET(fd_, &write)) { - out_->Flush(false); + SendFlush(); } if (FD_ISSET(fd_, &read)) { if (in_->Empty() && !in_->TryFill()) { @@ -462,7 +430,7 @@ bool WebSocketServer::ReadControlFrame(int opcode, size_t sz) { if (opcode == (int)Opcode::PING) { Pong(payload); // Try to send immediately if possible, but don't block. - out_->Flush(false); + SendFlush(); if (ping_) { ping_(payload); @@ -491,19 +459,19 @@ bool WebSocketServer::ReadControlFrame(int opcode, size_t sz) { void WebSocketServer::SendHeader(bool fin, int opcode, size_t sz) { assert((opcode & 0x0F) == opcode); uint8_t frameHeader = (fin ? 0x80 : 0x00) | opcode; - out_->Push((const char *)&frameHeader, 1); + SendBytes(&frameHeader, 1); // We never mask from the server. if (sz <= 125) { uint8_t frameSize = (int8_t)sz; - out_->Push((const char *)&frameSize, 1); + SendBytes(&frameSize, 1); } else if (sz <= 0xFFFF) { uint8_t frameSize[] = { 126, (uint8_t)((sz >> 8) & 0xFF), (uint8_t)((sz >> 0) & 0xFF), }; - out_->Push((const char *)frameSize, sizeof(frameSize)); + SendBytes(frameSize, sizeof(frameSize)); } else { uint64_t sz64 = sz; assert((sz64 & 0x8000000000000000ULL) == 0); @@ -518,7 +486,42 @@ void WebSocketServer::SendHeader(bool fin, int opcode, size_t sz) { (uint8_t)((sz64 >> 8) & 0xFF), (uint8_t)((sz64 >> 0) & 0xFF), }; - out_->Push((const char *)frameSize, sizeof(frameSize)); + SendBytes(frameSize, sizeof(frameSize)); + } +} + +void WebSocketServer::SendBytes(const void *p, size_t sz) { + const char *data = (const char *)p; + if (outBuf_.empty()) { + size_t pushed = out_->PushAtMost(data, sz); + data += pushed; + sz -= pushed; + } + + if (sz != 0) { + size_t pos = outBuf_.size(); + outBuf_.resize(pos + sz); + memcpy(&outBuf_[pos], data, sz); + } +} + +void WebSocketServer::SendFlush() { + out_->Flush(false); + + // Drain out as much of our buffer as possible. + size_t totalPushed = 0; + while (!outBuf_.empty()) { + size_t pushed = out_->PushAtMost((const char *)&outBuf_[totalPushed], outBuf_.size() - totalPushed); + if (pushed == 0) + break; + + totalPushed += pushed; + out_->Flush(false); + } + + if (totalPushed != 0) { + // Hopefully this is usually the entire buffer. + outBuf_.erase(outBuf_.begin(), outBuf_.begin() + totalPushed); } } diff --git a/ext/native/net/websocket_server.h b/ext/native/net/websocket_server.h index edd753fed6..98426f53cf 100644 --- a/ext/native/net/websocket_server.h +++ b/ext/native/net/websocket_server.h @@ -71,6 +71,8 @@ protected: } void SendHeader(bool fin, int opcode, size_t sz); + void SendBytes(const void *p, size_t sz); + void SendFlush(); bool ReadFrames(); bool ReadFrame(); bool ReadPending(); @@ -83,6 +85,7 @@ protected: InputSink *in_ = nullptr; OutputSink *out_ = nullptr; WebSocketClose closeReason_ = WebSocketClose::NO_STATUS; + std::vector outBuf_; std::vector pendingBuf_; uint8_t pendingMask_[4]{}; From 403632b4ca44035167e54cf26d3bfc42b588f6fb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 Apr 2018 20:02:44 -0700 Subject: [PATCH 155/301] http: Correct WebSocket close when code missing. --- ext/native/net/websocket_server.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index 2c7ec1cd54..fb430ee15f 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -188,14 +188,19 @@ void WebSocketServer::Pong(const std::vector &payload) { void WebSocketServer::Close(WebSocketClose reason) { closeReason_ = reason; - SendHeader(true, (int)Opcode::CLOSE, 2); + if (reason == WebSocketClose::NO_STATUS) { + // This means we received a CLOSE without a code. + SendHeader(true, (int)Opcode::CLOSE, 0); + } else { + SendHeader(true, (int)Opcode::CLOSE, 2); - uint16_t r = (uint16_t)reason; - uint8_t reasonData[] = { - (uint8_t)((r >> 8) & 0xFF), - (uint8_t)((r >> 0) & 0xFF), - }; - SendBytes((const char *)reasonData, sizeof(reasonData)); + uint16_t r = (uint16_t)reason; + uint8_t reasonData[] = { + (uint8_t)((r >> 8) & 0xFF), + (uint8_t)((r >> 0) & 0xFF), + }; + SendBytes((const char *)reasonData, sizeof(reasonData)); + } sentClose_ = true; } From 042256337a1e8be0d539b12217edfbe4233f7e7a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 Apr 2018 20:30:12 -0700 Subject: [PATCH 156/301] thread: Add a simple thread executor. --- ext/native/thread/executor.cpp | 7 ++++++- ext/native/thread/executor.h | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ext/native/thread/executor.cpp b/ext/native/thread/executor.cpp index 8c5d6cd097..197f9d7920 100644 --- a/ext/native/thread/executor.cpp +++ b/ext/native/thread/executor.cpp @@ -1,11 +1,16 @@ #include "thread/executor.h" #include +#include namespace threading { void SameThreadExecutor::Run(std::function func) { - func(); + func(); +} + +void NewThreadExecutor::Run(std::function func) { + std::thread(func).detach(); } } // namespace threading diff --git a/ext/native/thread/executor.h b/ext/native/thread/executor.h index 7a5b5286a1..6d6515920c 100644 --- a/ext/native/thread/executor.h +++ b/ext/native/thread/executor.h @@ -6,13 +6,18 @@ namespace threading { // Stuff that can execute other stuff, like threadpools, should inherit from this. class Executor { - public: - virtual void Run(std::function func) = 0; +public: + virtual void Run(std::function func) = 0; }; class SameThreadExecutor : public Executor { - public: - virtual void Run(std::function func); +public: + void Run(std::function func) override; +}; + +class NewThreadExecutor : public Executor { +public: + void Run(std::function func) override; }; } // namespace threading From 40c7f5ddfd79723b9acd6755750e99319f015e05 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 2 Jun 2018 16:32:04 -0700 Subject: [PATCH 157/301] UI: Make button padding configurable. Useful for buttons on the MainScreen which look out of place with the default padding. --- ext/native/ui/view.cpp | 4 ++-- ext/native/ui/view.h | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/native/ui/view.cpp b/ext/native/ui/view.cpp index 58427c8715..5f1deac264 100644 --- a/ext/native/ui/view.cpp +++ b/ext/native/ui/view.cpp @@ -733,8 +733,8 @@ void Button::GetContentDimensions(const UIContext &dc, float &w, float &h) const dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, text_.c_str(), &w, &h); } // Add some internal padding to not look totally ugly - w += 16; - h += 8; + w += paddingW_; + h += paddingH_; } void Button::Draw(UIContext &dc) { diff --git a/ext/native/ui/view.h b/ext/native/ui/view.h index 05e3100ab0..8c24b2fadb 100644 --- a/ext/native/ui/view.h +++ b/ext/native/ui/view.h @@ -508,11 +508,17 @@ public: void Draw(UIContext &dc) override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; const std::string &GetText() const { return text_; } + void SetPadding(int w, int h) { + paddingW_ = w; + paddingH_ = h; + } private: Style style_; std::string text_; ImageID imageID_; + int paddingW_ = 16; + int paddingH_ = 8; }; class Slider : public Clickable { From 640eadb0f7b84e6b131d1eca651ae67858833b68 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 2 Jun 2018 16:32:55 -0700 Subject: [PATCH 158/301] UI: Show warning when in a temporary dir. In case PPSSPP is opened from a compressed folder, or otherwise from a temporary dir, warn users that the saves won't be permanent. --- UI/MainScreen.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++---- UI/MainScreen.h | 1 + 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index f3276a41e8..cbd9bf8a2d 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -29,6 +29,7 @@ #include "ui/ui_context.h" #include "ui/view.h" #include "ui/viewgroup.h" +#include "util/text/utf8.h" #include "Common/FileUtil.h" #include "Core/System.h" @@ -76,6 +77,47 @@ bool MainScreen::showHomebrewTab = false; +static bool IsTempPath(const std::string &str) { + std::string item = str; + + const auto testPath = [&](std::string temp) { +#ifdef _WIN32 + temp = ReplaceAll(temp, "/", "\\"); + if (!temp.empty() && temp[temp.size() - 1] != '\\') + temp += "\\"; +#else + if (!temp.empty() && temp[temp.size() - 1] != '/') + temp += "/"; +#endif + return startsWith(item, temp); + }; + + const auto testCPath = [&](const char *temp) { + if (temp && temp[0]) + return testPath(temp); + return false; + }; + +#ifdef _WIN32 + // Normalize slashes. + item = ReplaceAll(str, "/", "\\"); + + wchar_t tempPath[MAX_PATH]; + GetTempPath(MAX_PATH, tempPath); + if (testPath(ConvertWStringToUTF8(tempPath))) + return true; +#endif + + if (testCPath(getenv("TMPDIR"))) + return true; + if (testCPath(getenv("TMP"))) + return true; + if (testCPath(getenv("TEMP"))) + return true; + + return false; +} + class GameButton : public UI::Clickable { public: GameButton(const std::string &gamePath, bool gridStyle, UI::LayoutParams *layoutParams = 0) @@ -743,6 +785,7 @@ void MainScreen::CreateViews() { bool showRecent = g_Config.iMaxRecent > 0; bool hasStorageAccess = System_GetPermissionStatus(SYSTEM_PERMISSION_STORAGE) == PERMISSION_STATUS_GRANTED; + bool storageIsTemporary = IsTempPath(GetSysDirectory(DIRECTORY_SAVEDATA)) && !confirmedTemporary_; if (showRecent && !hasStorageAccess) { showRecent = !g_Config.recentIsos.empty(); } @@ -762,7 +805,7 @@ void MainScreen::CreateViews() { tabRecentGames->OnHighlight.Handle(this, &MainScreen::OnGameHighlight); } - Button *grantStorageButton = nullptr; + Button *focusButton = nullptr; if (hasStorageAccess) { ScrollView *scrollAllGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollAllGames->SetTag("MainScreenAllGames"); @@ -810,6 +853,26 @@ void MainScreen::CreateViews() { backFromStore_ = false; showHomebrewTab = false; } + + if (storageIsTemporary) { + LinearLayout *buttonHolder = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + buttonHolder->Add(new Spacer(new LinearLayoutParams(1.0f))); + focusButton = new Button(mm->T("SavesAreTemporaryIgnore", "Ignore warning"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + focusButton->SetPadding(32, 16); + buttonHolder->Add(focusButton)->OnClick.Add([this](UI::EventParams &e) { + confirmedTemporary_ = true; + RecreateViews(); + return UI::EVENT_DONE; + }); + buttonHolder->Add(new Spacer(new LinearLayoutParams(1.0f))); + + leftColumn->Add(new Spacer(new LinearLayoutParams(0.1f))); + leftColumn->Add(new TextView(mm->T("SavesAreTemporary", "PPSSPP saving in temporary storage"), ALIGN_HCENTER, false)); + leftColumn->Add(new TextView(mm->T("SavesAreTemporaryGuidance", "Extract PPSSPP somewhere to save permanently"), ALIGN_HCENTER, false)); + leftColumn->Add(new Spacer(10.0f)); + leftColumn->Add(buttonHolder); + leftColumn->Add(new Spacer(new LinearLayoutParams(0.1f))); + } } else { if (!showRecent) { leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f)); @@ -820,8 +883,9 @@ void MainScreen::CreateViews() { LinearLayout *buttonHolder = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); buttonHolder->Add(new Spacer(new LinearLayoutParams(1.0f))); - grantStorageButton = new Button(mm->T("Give PPSSPP permission to access storage"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - buttonHolder->Add(grantStorageButton)->OnClick.Handle(this, &MainScreen::OnAllowStorage); + focusButton = new Button(mm->T("Give PPSSPP permission to access storage"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + focusButton->SetPadding(32, 16); + buttonHolder->Add(focusButton)->OnClick.Handle(this, &MainScreen::OnAllowStorage); buttonHolder->Add(new Spacer(new LinearLayoutParams(1.0f))); leftColumn->Add(new Spacer(new LinearLayoutParams(0.1f))); @@ -880,8 +944,8 @@ void MainScreen::CreateViews() { root_->Add(rightColumn); } - if (grantStorageButton) { - root_->SetDefaultFocusView(grantStorageButton); + if (focusButton) { + root_->SetDefaultFocusView(focusButton); } else if (tabHolder_->GetVisibility() != V_GONE) { root_->SetDefaultFocusView(tabHolder_); } diff --git a/UI/MainScreen.h b/UI/MainScreen.h index b80bfae5e9..d4957e1d7a 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -119,6 +119,7 @@ protected: bool backFromStore_; bool lockBackgroundAudio_; bool lastVertical_; + bool confirmedTemporary_ = false; friend class RemoteISOBrowseScreen; }; From 3a8a2ff9330bedb025716c76d91efbb6ac955daa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 21 Apr 2018 13:51:18 -0700 Subject: [PATCH 159/301] http: Move web server to core and use flags. This way we can independently control the debugger and disc sharing, since debugger could have security impact. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/WebServer.cpp | 278 ++++++++++++++++++++++++++++++++++++ Core/WebServer.h | 27 ++++ UI/NativeApp.cpp | 4 +- UI/RemoteISOScreen.cpp | 209 ++------------------------- UI/RemoteISOScreen.h | 2 - UWP/CoreUWP/CoreUWP.vcxproj | 2 + android/jni/Android.mk | 1 + 10 files changed, 329 insertions(+), 204 deletions(-) create mode 100644 Core/WebServer.cpp create mode 100644 Core/WebServer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ea354847c9..1e0db28925 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1393,6 +1393,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HDRemaster.cpp Core/HDRemaster.h Core/ThreadEventQueue.h + Core/WebServer.cpp + Core/WebServer.h Core/Debugger/Breakpoints.cpp Core/Debugger/Breakpoints.h Core/Debugger/DebugInterface.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 209890e9f1..b068e321e9 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -511,6 +511,7 @@ AnySuitable + @@ -746,6 +747,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 2efbdbd29a..fe637033f8 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -689,6 +689,9 @@ Core + + Core + @@ -1268,6 +1271,9 @@ Core + + Core + diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp new file mode 100644 index 0000000000..6b20646f2e --- /dev/null +++ b/Core/WebServer.cpp @@ -0,0 +1,278 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include +#include +#include "base/stringutil.h" +#include "base/timeutil.h" +#include "file/fd_util.h" +#include "net/http_client.h" +#include "net/http_server.h" +#include "net/sinks.h" +#include "thread/threadutil.h" +#include "Common/FileUtil.h" +#include "Common/Log.h" +#include "Core/Config.h" +#include "Core/WebServer.h" + +enum class ServerStatus { + STOPPED, + STARTING, + RUNNING, + STOPPING, + RESTARTING, +}; + +static const char *REPORT_HOSTNAME = "report.ppsspp.org"; +static const int REPORT_PORT = 80; + +static std::thread serverThread; +static ServerStatus serverStatus; +static std::mutex serverStatusLock; +static int serverFlags; + +static void UpdateStatus(ServerStatus s) { + std::lock_guard guard(serverStatusLock); + serverStatus = s; +} + +static bool UpdateStatus(ServerStatus s, ServerStatus old) { + std::lock_guard guard(serverStatusLock); + if (serverStatus == old) { + serverStatus = s; + return true; + } + return false; +} + +static ServerStatus RetrieveStatus() { + std::lock_guard guard(serverStatusLock); + return serverStatus; +} + +// This reports the local IP address to report.ppsspp.org, which can then +// relay that address to a mobile device searching for the server. +static void RegisterServer(int port) { + http::Client http; + Buffer theVoid; + + char resource4[1024] = {}; + if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) { + if (http.Connect()) { + std::string ip = fd_util::GetLocalIP(http.sock()); + snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); + + http.GET(resource4, &theVoid); + theVoid.Skip(theVoid.size()); + http.Disconnect(); + } + } + + if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) { + // We register both IPv4 and IPv6 in case the other client is using a different one. + if (resource4[0] != 0 && http.Connect()) { + http.GET(resource4, &theVoid); + theVoid.Skip(theVoid.size()); + http.Disconnect(); + } + + // Currently, we're not using keepalive, so gotta reconnect... + if (http.Connect()) { + char resource6[1024] = {}; + std::string ip = fd_util::GetLocalIP(http.sock()); + snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); + + http.GET(resource6, &theVoid); + theVoid.Skip(theVoid.size()); + http.Disconnect(); + } + } +} + +static void RegisterDiscHandlers(http::Server *http, std::unordered_map *paths) { + for (std::string filename : g_Config.recentIsos) { +#ifdef _WIN32 + static const std::string sep = "\\/"; +#else + static const std::string sep = "/"; +#endif + size_t basepos = filename.find_last_of(sep); + std::string basename = "/" + (basepos == filename.npos ? filename : filename.substr(basepos + 1)); + + // Let's not serve directories, since they won't work. Only single files. + // Maybe can do PBPs and other files later. Would be neat to stream virtual disc filesystems. + if (endsWithNoCase(basename, ".cso") || endsWithNoCase(basename, ".iso")) { + (*paths)[ReplaceAll(basename, " ", "%20")] = filename; + } + } + + auto handler = [paths](const http::Request &request) { + std::string filename = (*paths)[request.resource()]; + s64 sz = File::GetFileSize(filename); + + std::string range; + if (request.Method() == http::RequestHeader::HEAD) { + request.WriteHttpResponseHeader(200, sz, "application/octet-stream", "Accept-Ranges: bytes\r\n"); + } else if (request.GetHeader("range", &range)) { + s64 begin = 0, last = 0; + if (sscanf(range.c_str(), "bytes=%lld-%lld", &begin, &last) != 2) { + request.WriteHttpResponseHeader(400, -1, "text/plain"); + request.Out()->Push("Could not understand range request."); + return; + } + + if (begin < 0 || begin > last || last >= sz) { + request.WriteHttpResponseHeader(416, -1, "text/plain"); + request.Out()->Push("Range goes outside of file."); + return; + } + + FILE *fp = File::OpenCFile(filename, "rb"); + if (!fp || fseek(fp, begin, SEEK_SET) != 0) { + request.WriteHttpResponseHeader(500, -1, "text/plain"); + request.Out()->Push("File access failed."); + if (fp) { + fclose(fp); + } + return; + } + + s64 len = last - begin + 1; + char contentRange[1024]; + sprintf(contentRange, "Content-Range: bytes %lld-%lld/%lld\r\n", begin, last, sz); + request.WriteHttpResponseHeader(206, len, "application/octet-stream", contentRange); + + const size_t CHUNK_SIZE = 16 * 1024; + char *buf = new char[CHUNK_SIZE]; + for (s64 pos = 0; pos < len; pos += CHUNK_SIZE) { + s64 chunklen = std::min(len - pos, (s64)CHUNK_SIZE); + fread(buf, chunklen, 1, fp); + request.Out()->Push(buf, chunklen); + } + fclose(fp); + delete [] buf; + request.Out()->Flush(); + } else { + request.WriteHttpResponseHeader(418, -1, "text/plain"); + request.Out()->Push("This server only supports range requests."); + } + }; + + for (auto pair : *paths) { + http->RegisterHandler(pair.first.c_str(), handler); + } +} + +static void ExecuteWebServer() { + setCurrentThreadName("HTTPServer"); + + auto http = new http::Server(new threading::NewThreadExecutor()); + std::unordered_map discPaths; + + if (serverFlags & (int)WebServerFlags::DISCS) { + RegisterDiscHandlers(http, &discPaths); + } + + if (!http->Listen(g_Config.iRemoteISOPort)) { + if (!http->Listen(0)) { + ERROR_LOG(FILESYS, "Unable to listen on any port"); + UpdateStatus(ServerStatus::STOPPED); + return; + } + } + UpdateStatus(ServerStatus::RUNNING); + + g_Config.iRemoteISOPort = http->Port(); + RegisterServer(http->Port()); + double lastRegister = real_time_now(); + while (RetrieveStatus() == ServerStatus::RUNNING) { + http->RunSlice(1.0); + + double now = real_time_now(); + if (now > lastRegister + 540.0) { + RegisterServer(http->Port()); + lastRegister = now; + } + } + + http->Stop(); + + // Move to STARTING to lock flags/STOPPING. + if (UpdateStatus(ServerStatus::STARTING, ServerStatus::RESTARTING)) { + ExecuteWebServer(); + } else { + UpdateStatus(ServerStatus::STOPPED); + } +} + +bool StartWebServer(WebServerFlags flags) { + std::lock_guard guard(serverStatusLock); + switch (serverStatus) { + case ServerStatus::RUNNING: + case ServerStatus::RESTARTING: + if ((serverFlags & (int)flags) == (int)flags) { + // Already running those flags. + return false; + } + serverStatus = ServerStatus::RESTARTING; + serverFlags |= (int)flags; + return true; + + case ServerStatus::STOPPED: + serverStatus = ServerStatus::STARTING; + serverFlags = (int)flags; + serverThread = std::thread(&ExecuteWebServer); + serverThread.detach(); + return true; + + default: + return false; + } +} + +bool StopWebServer(WebServerFlags flags) { + std::lock_guard guard(serverStatusLock); + if (serverStatus != ServerStatus::RUNNING && serverStatus != ServerStatus::RESTARTING) { + return false; + } + + serverFlags &= ~(int)flags; + if (serverFlags == 0) { + serverStatus = ServerStatus::STOPPING; + } else { + serverStatus = ServerStatus::RESTARTING; + } + return true; +} + +bool WebServerStopping(WebServerFlags flags) { + std::lock_guard guard(serverStatusLock); + if (serverStatus == ServerStatus::RESTARTING) { + return (serverFlags & (int)flags) == 0; + } + return serverStatus == ServerStatus::STOPPING; +} + +bool WebServerStopped(WebServerFlags flags) { + std::lock_guard guard(serverStatusLock); + if (serverStatus == ServerStatus::RUNNING) { + return (serverFlags & (int)flags) == 0; + } + return serverStatus == ServerStatus::STOPPED; +} diff --git a/Core/WebServer.h b/Core/WebServer.h new file mode 100644 index 0000000000..db0967cbf2 --- /dev/null +++ b/Core/WebServer.h @@ -0,0 +1,27 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +enum class WebServerFlags { + DISCS = 1, + + ALL = 1, +}; + +bool StartWebServer(WebServerFlags flags); +bool StopWebServer(WebServerFlags flags); +bool WebServerStopping(WebServerFlags flags); +bool WebServerStopped(WebServerFlags flags); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 7130c7d181..8ae0bedb41 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -84,6 +84,7 @@ #include "Core/HLE/sceUsbGps.h" #include "Core/Util/GameManager.h" #include "Core/Util/AudioFormat.h" +#include "Core/WebServer.h" #include "GPU/GPUInterface.h" #include "ui_atlas.h" @@ -587,7 +588,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } if (g_Config.bRemoteShareOnStartup) { - StartRemoteISOSharing(); + // TODO: Separate config options. + StartWebServer(WebServerFlags::ALL); } std::string sysName = System_GetProperty(SYSPROP_NAME); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 9831701e17..31d8f9f966 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -18,20 +18,15 @@ #include #include #include -#include #include "base/timeutil.h" -#include "file/fd_util.h" #include "i18n/i18n.h" #include "json/json_reader.h" #include "net/http_client.h" -#include "net/http_server.h" #include "net/resolve.h" -#include "net/sinks.h" -#include "thread/threadutil.h" #include "Common/Common.h" -#include "Common/FileUtil.h" #include "Core/Config.h" +#include "Core/WebServer.h" #include "UI/RemoteISOScreen.h" using namespace UI; @@ -39,190 +34,9 @@ using namespace UI; static const char *REPORT_HOSTNAME = "report.ppsspp.org"; static const int REPORT_PORT = 80; -enum class ServerStatus { - STOPPED, - STARTING, - RUNNING, - STOPPING, -}; - -static std::thread *serverThread = nullptr; -static ServerStatus serverStatus; -static std::mutex serverStatusLock; -static std::condition_variable serverStatusCond; - static bool scanCancelled = false; static bool scanAborted = false; -static void UpdateStatus(ServerStatus s) { - std::lock_guard guard(serverStatusLock); - serverStatus = s; - serverStatusCond.notify_one(); -} - -static ServerStatus RetrieveStatus() { - std::lock_guard guard(serverStatusLock); - return serverStatus; -} - -// This reports the local IP address to report.ppsspp.org, which can then -// relay that address to a mobile device searching for the server. -static void RegisterServer(int port) { - http::Client http; - Buffer theVoid; - - char resource4[1024] = {}; - if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) { - if (http.Connect(2, 20.0, &scanCancelled)) { - std::string ip = fd_util::GetLocalIP(http.sock()); - snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - - http.GET(resource4, &theVoid); - theVoid.Skip(theVoid.size()); - http.Disconnect(); - } - } - - if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) { - // We register both IPv4 and IPv6 in case the other client is using a different one. - if (resource4[0] != 0 && http.Connect()) { - http.GET(resource4, &theVoid); - theVoid.Skip(theVoid.size()); - http.Disconnect(); - } - - // Currently, we're not using keepalive, so gotta reconnect... - if (http.Connect()) { - char resource6[1024] = {}; - std::string ip = fd_util::GetLocalIP(http.sock()); - snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - - http.GET(resource6, &theVoid); - theVoid.Skip(theVoid.size()); - http.Disconnect(); - } - } -} - -static void ExecuteServer() { - setCurrentThreadName("HTTPServer"); - - auto http = new http::Server(new threading::SameThreadExecutor()); - - std::map paths; - for (std::string filename : g_Config.recentIsos) { -#ifdef _WIN32 - static const std::string sep = "\\/"; -#else - static const std::string sep = "/"; -#endif - size_t basepos = filename.find_last_of(sep); - std::string basename = "/" + (basepos == filename.npos ? filename : filename.substr(basepos + 1)); - - // Let's not serve directories, since they won't work. Only single files. - // Maybe can do PBPs and other files later. Would be neat to stream virtual disc filesystems. - if (endsWithNoCase(basename, ".cso") || endsWithNoCase(basename, ".iso")) { - paths[ReplaceAll(basename, " ", "%20")] = filename; - } - } - - auto handler = [&](const http::Request &request) { - std::string filename = paths[request.resource()]; - s64 sz = File::GetFileSize(filename); - - std::string range; - if (request.Method() == http::RequestHeader::HEAD) { - request.WriteHttpResponseHeader(200, sz, "application/octet-stream", "Accept-Ranges: bytes\r\n"); - } else if (request.GetHeader("range", &range)) { - s64 begin = 0, last = 0; - if (sscanf(range.c_str(), "bytes=%lld-%lld", &begin, &last) != 2) { - request.WriteHttpResponseHeader(400, -1, "text/plain"); - request.Out()->Push("Could not understand range request."); - return; - } - - if (begin < 0 || begin > last || last >= sz) { - request.WriteHttpResponseHeader(416, -1, "text/plain"); - request.Out()->Push("Range goes outside of file."); - return; - } - - FILE *fp = File::OpenCFile(filename, "rb"); - if (!fp || fseek(fp, begin, SEEK_SET) != 0) { - request.WriteHttpResponseHeader(500, -1, "text/plain"); - request.Out()->Push("File access failed."); - if (fp) { - fclose(fp); - } - return; - } - - s64 len = last - begin + 1; - char contentRange[1024]; - sprintf(contentRange, "Content-Range: bytes %lld-%lld/%lld\r\n", begin, last, sz); - request.WriteHttpResponseHeader(206, len, "application/octet-stream", contentRange); - - const size_t CHUNK_SIZE = 16 * 1024; - char *buf = new char[CHUNK_SIZE]; - for (s64 pos = 0; pos < len; pos += CHUNK_SIZE) { - s64 chunklen = std::min(len - pos, (s64)CHUNK_SIZE); - fread(buf, chunklen, 1, fp); - request.Out()->Push(buf, chunklen); - } - fclose(fp); - delete [] buf; - request.Out()->Flush(); - } else { - request.WriteHttpResponseHeader(418, -1, "text/plain"); - request.Out()->Push("This server only supports range requests."); - } - }; - - for (auto pair : paths) { - http->RegisterHandler(pair.first.c_str(), handler); - } - - if (!http->Listen(g_Config.iRemoteISOPort)) { - if (!http->Listen(0)) { - ERROR_LOG(FILESYS, "Unable to listen on any port"); - UpdateStatus(ServerStatus::STOPPED); - return; - } - } - UpdateStatus(ServerStatus::RUNNING); - - g_Config.iRemoteISOPort = http->Port(); - RegisterServer(http->Port()); - double lastRegister = real_time_now(); - while (RetrieveStatus() == ServerStatus::RUNNING) { - http->RunSlice(5.0); - - double now = real_time_now(); - if (now > lastRegister + 540.0) { - RegisterServer(http->Port()); - lastRegister = now; - } - } - - http->Stop(); - - UpdateStatus(ServerStatus::STOPPED); -} - -bool StartRemoteISOSharing() { - std::lock_guard guard(serverStatusLock); - - if (serverStatus != ServerStatus::STOPPED) { - return false; - } - - serverStatus = ServerStatus::STARTING; - serverThread = new std::thread(&ExecuteServer); - serverThread->detach(); - - return true; -} - static bool FindServer(std::string &resultHost, int &resultPort) { http::Client http; Buffer result; @@ -393,11 +207,8 @@ RemoteISOScreen::RemoteISOScreen() : serverRunning_(false), serverStopping_(fals void RemoteISOScreen::update() { UIScreenWithBackground::update(); - bool nowRunning = RetrieveStatus() != ServerStatus::STOPPED; + bool nowRunning = !WebServerStopped(WebServerFlags::DISCS); if (serverStopping_ && !nowRunning) { - // Server stopped, delete the thread. - delete serverThread; - serverThread = nullptr; serverStopping_ = false; } @@ -424,11 +235,10 @@ void RemoteISOScreen::CreateViews() { rightColumnItems->SetSpacing(0.0f); Choice *browseChoice = new Choice(ri->T("Browse Games")); rightColumnItems->Add(browseChoice)->OnClick.Handle(this, &RemoteISOScreen::HandleBrowse); - ServerStatus status = RetrieveStatus(); - if (status == ServerStatus::STOPPING) { + if (WebServerStopping(WebServerFlags::DISCS)) { rightColumnItems->Add(new Choice(ri->T("Stopping..")))->SetDisabledPtr(&serverStopping_); browseChoice->SetEnabled(false); - } else if (status != ServerStatus::STOPPED) { + } else if (!WebServerStopped(WebServerFlags::DISCS)) { rightColumnItems->Add(new Choice(ri->T("Stop Sharing")))->OnClick.Handle(this, &RemoteISOScreen::HandleStopServer); browseChoice->SetEnabled(false); } else { @@ -450,7 +260,7 @@ void RemoteISOScreen::CreateViews() { } UI::EventReturn RemoteISOScreen::HandleStartServer(UI::EventParams &e) { - if (!StartRemoteISOSharing()) { + if (!StartWebServer(WebServerFlags::DISCS)) { return EVENT_SKIPPED; } @@ -458,13 +268,10 @@ UI::EventReturn RemoteISOScreen::HandleStartServer(UI::EventParams &e) { } UI::EventReturn RemoteISOScreen::HandleStopServer(UI::EventParams &e) { - std::lock_guard guard(serverStatusLock); - - if (serverStatus != ServerStatus::RUNNING) { + if (!StopWebServer(WebServerFlags::DISCS)) { return EVENT_SKIPPED; } - serverStatus = ServerStatus::STOPPING; serverStopping_ = true; RecreateViews(); @@ -683,13 +490,13 @@ void RemoteISOBrowseScreen::CreateViews() { } RemoteISOSettingsScreen::RemoteISOSettingsScreen() { - serverRunning_ = RetrieveStatus() != ServerStatus::STOPPED; + serverRunning_ = !WebServerStopped(WebServerFlags::DISCS); } void RemoteISOSettingsScreen::update() { UIDialogScreenWithBackground::update(); - bool nowRunning = RetrieveStatus() != ServerStatus::STOPPED; + bool nowRunning = !WebServerStopped(WebServerFlags::DISCS); if (serverRunning_ != nowRunning) { RecreateViews(); } diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index a4384d793e..b7f7ff0f5c 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -25,8 +25,6 @@ #include "UI/MiscScreens.h" #include "UI/MainScreen.h" -bool StartRemoteISOSharing(); - class RemoteISOScreen : public UIScreenWithBackground { public: RemoteISOScreen(); diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 04eba6a66f..7cd70cfa70 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -463,6 +463,7 @@ + @@ -673,6 +674,7 @@ + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 2c081f2ca7..428134fbcb 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -297,6 +297,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Screenshot.cpp \ $(SRC)/Core/System.cpp \ $(SRC)/Core/TextureReplacer.cpp \ + $(SRC)/Core/WebServer.cpp \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ From df72abcd6257d643d878a5f1ed9d17114c4c3841 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 7 Jun 2018 23:22:28 -0700 Subject: [PATCH 160/301] net: Use WSAAddressToString for Windows XP. Unfortunately, we don't have inet_ntop on older Windows. --- ext/native/file/fd_util.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ext/native/file/fd_util.cpp b/ext/native/file/fd_util.cpp index b61703c263..0490c86a3e 100644 --- a/ext/native/file/fd_util.cpp +++ b/ext/native/file/fd_util.cpp @@ -141,17 +141,29 @@ std::string GetLocalIP(int sock) { memset(&server_addr, 0, sizeof(server_addr)); socklen_t len = sizeof(server_addr); if (getsockname(sock, (struct sockaddr *)&server_addr, &len) == 0) { - char temp[64]; + char temp[64]{}; + + // We clear the port below for WSAAddressToStringA. void *addr; if (server_addr.sa.sa_family == AF_INET6) { + server_addr.ipv6.sin6_port = 0; addr = &server_addr.ipv6.sin6_addr; } else { + server_addr.ipv4.sin_port = 0; addr = &server_addr.ipv4.sin_addr; } +#ifdef _WIN32 + DWORD len = (DWORD)sizeof(temp); + // Windows XP doesn't support inet_ntop. + if (WSAAddressToStringA((struct sockaddr *)&server_addr, sizeof(server_addr), nullptr, temp, &len) == 0) { + return temp; + } +#else const char *result = inet_ntop(server_addr.sa.sa_family, addr, temp, sizeof(temp)); if (result) { return result; } +#endif } return ""; } From 79b3f81b0ce29094263c4d43a82382e3aac5bf69 Mon Sep 17 00:00:00 2001 From: Benjamin Hodgetts Date: Fri, 8 Jun 2018 12:11:05 +0100 Subject: [PATCH 161/301] Add missing include Fixes issue https://github.com/hrydgard/ppsspp/issues/11157 --- ext/native/net/websocket_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index fb430ee15f..0302ddbb22 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #ifndef _WIN32 #include From c2b9b5a9e81322dfb1bd0aab26e29174a123ce51 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 12 Apr 2018 22:11:11 -0700 Subject: [PATCH 162/301] Debugger: Serve a simple log listener WebSocket. Planning to add more functionality to it, and sharing the reporting port is possibly not ideal - although it would make discovery easier. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 ++ Core/Debugger/WebSocket.cpp | 127 ++++++++++++++++++++++++++++++++++++ Core/Debugger/WebSocket.h | 24 +++++++ Core/WebServer.cpp | 4 ++ Core/WebServer.h | 3 +- android/jni/Android.mk | 1 + 8 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 Core/Debugger/WebSocket.cpp create mode 100644 Core/Debugger/WebSocket.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e0db28925..c4ef3860bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1402,6 +1402,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/SymbolMap.h Core/Debugger/DisassemblyManager.cpp Core/Debugger/DisassemblyManager.h + Core/Debugger/WebSocket.cpp + Core/Debugger/WebSocket.h Core/Dialog/PSPDialog.cpp Core/Dialog/PSPDialog.h Core/Dialog/PSPGamedataInstallDialog.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b068e321e9..0d5a5c1940 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -184,6 +184,7 @@ + @@ -532,6 +533,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index fe637033f8..914b2e7ea1 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -692,6 +692,9 @@ Core + + Debugger + @@ -1274,6 +1277,9 @@ Core + + Debugger + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp new file mode 100644 index 0000000000..3d9cb95a6f --- /dev/null +++ b/Core/Debugger/WebSocket.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include "json/json_writer.h" +#include "net/websocket_server.h" +#include "Core/Debugger/WebSocket.h" +#include "Common/LogManager.h" + +// TODO: Move this to its own file? +class DebuggerLogListener : public LogListener { +public: + void Log(const LogMessage &msg) override { + std::lock_guard guard(lock_); + messages_[nextMessage_] = msg; + nextMessage_++; + if (nextMessage_ >= BUFFER_SIZE) + nextMessage_ -= BUFFER_SIZE; + count_++; + } + + std::vector GetMessages() { + std::lock_guard guard(lock_); + int splitPoint; + int readCount; + if (read_ + BUFFER_SIZE < count_) { + // We'll start with our oldest then. + splitPoint = nextMessage_; + readCount = Count(); + } else { + splitPoint = read_; + readCount = count_ - read_; + } + + read_ = count_; + + std::vector results; + int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE); + for (int i = splitPoint; i < splitEnd; ++i) { + results.push_back(messages_[i]); + readCount--; + } + for (int i = 0; i < readCount; ++i) { + results.push_back(messages_[i]); + } + + return results; + } + + int Count() const { + return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE; + } + +private: + enum { BUFFER_SIZE = 128 }; + LogMessage messages_[BUFFER_SIZE]; + std::mutex lock_; + int nextMessage_ = 0; + int count_ = 0; + int read_ = 0; +}; + +struct DebuggerLogEvent { + std::string header; + std::string message; + int level; + const char *channel; + + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", "log"); + j.writeString("header", header); + j.writeString("message", message); + j.writeInt("level", level); + j.writeString("channel", channel); + j.end(); + return j.str(); + } +}; + +void HandleDebuggerRequest(const http::Request &request) { + net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org"); + if (!ws) + return; + + DebuggerLogListener *logListener = new DebuggerLogListener(); + if (LogManager::GetInstance()) + LogManager::GetInstance()->AddListener(logListener); + + // TODO: Handle incoming messages. + ws->SetTextHandler([&](const std::string &t) { + ws->Send(R"({"event":"error","message":"Bad message","level":2})"); + }); + ws->SetBinaryHandler([&](const std::vector &d) { + ws->Send(R"({"event":"error","message":"Bad message","level":2})"); + }); + + while (ws->Process(0.1f)) { + auto messages = logListener->GetMessages(); + // TODO: Check for other conditions? + for (auto msg : messages) { + ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log}); + } + continue; + } + + if (LogManager::GetInstance()) + LogManager::GetInstance()->RemoveListener(logListener); + delete logListener; + delete ws; +} diff --git a/Core/Debugger/WebSocket.h b/Core/Debugger/WebSocket.h new file mode 100644 index 0000000000..73b4e5fe8f --- /dev/null +++ b/Core/Debugger/WebSocket.h @@ -0,0 +1,24 @@ +// Copyright (c) 2017- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +namespace http { +class Request; +} + +void HandleDebuggerRequest(const http::Request &request); diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 6b20646f2e..d0f7c39fe7 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -29,6 +29,7 @@ #include "Common/FileUtil.h" #include "Common/Log.h" #include "Core/Config.h" +#include "Core/Debugger/WebSocket.h" #include "Core/WebServer.h" enum class ServerStatus { @@ -188,6 +189,9 @@ static void ExecuteWebServer() { if (serverFlags & (int)WebServerFlags::DISCS) { RegisterDiscHandlers(http, &discPaths); } + if (serverFlags & (int)WebServerFlags::DEBUGGER) { + http->RegisterHandler("/debugger", &HandleDebuggerRequest); + } if (!http->Listen(g_Config.iRemoteISOPort)) { if (!http->Listen(0)) { diff --git a/Core/WebServer.h b/Core/WebServer.h index db0967cbf2..07c2efb4f0 100644 --- a/Core/WebServer.h +++ b/Core/WebServer.h @@ -17,8 +17,9 @@ enum class WebServerFlags { DISCS = 1, + DEBUGGER = 2, - ALL = 1, + ALL = 1 | 2, }; bool StartWebServer(WebServerFlags flags); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 428134fbcb..2d6fa3f9f6 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -300,6 +300,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/WebServer.cpp \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ + $(SRC)/Core/Debugger/WebSocket.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ $(SRC)/Core/Dialog/PSPMsgDialog.cpp \ From b37d59e8fab30d5cb514e2eab591dd7d10b6cde2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 08:54:04 -0700 Subject: [PATCH 163/301] Debugger: Organize WebSocket event handling. Just a starting point to organize it. Trying to keep it simple. --- CMakeLists.txt | 7 + Core/Core.vcxproj | 7 + Core/Core.vcxproj.filters | 24 ++++ Core/Debugger/WebSocket.cpp | 130 ++++++------------ Core/Debugger/WebSocket/Common.h | 42 ++++++ Core/Debugger/WebSocket/GameBroadcaster.cpp | 38 +++++ Core/Debugger/WebSocket/GameBroadcaster.h | 36 +++++ Core/Debugger/WebSocket/LogBroadcaster.cpp | 112 +++++++++++++++ Core/Debugger/WebSocket/LogBroadcaster.h | 35 +++++ .../WebSocket/SteppingBroadcaster.cpp | 32 +++++ Core/Debugger/WebSocket/SteppingBroadcaster.h | 36 +++++ android/jni/Android.mk | 3 + 12 files changed, 412 insertions(+), 90 deletions(-) create mode 100644 Core/Debugger/WebSocket/Common.h create mode 100644 Core/Debugger/WebSocket/GameBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/GameBroadcaster.h create mode 100644 Core/Debugger/WebSocket/LogBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/LogBroadcaster.h create mode 100644 Core/Debugger/WebSocket/SteppingBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/SteppingBroadcaster.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c4ef3860bf..2265f2b5de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1404,6 +1404,13 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/DisassemblyManager.h Core/Debugger/WebSocket.cpp Core/Debugger/WebSocket.h + Core/Debugger/WebSocket/Common.h + Core/Debugger/WebSocket/GameBroadcaster.cpp + Core/Debugger/WebSocket/GameBroadcaster.h + Core/Debugger/WebSocket/LogBroadcaster.cpp + Core/Debugger/WebSocket/LogBroadcaster.h + Core/Debugger/WebSocket/SteppingBroadcaster.cpp + Core/Debugger/WebSocket/SteppingBroadcaster.h Core/Dialog/PSPDialog.cpp Core/Dialog/PSPDialog.h Core/Dialog/PSPGamedataInstallDialog.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 0d5a5c1940..e713cd5057 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -185,6 +185,9 @@ + + + @@ -534,6 +537,10 @@ + + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 914b2e7ea1..3449fa0158 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -70,6 +70,9 @@ {119ac973-e457-4025-9e1e-4fb34022ae23} + + {c21d9bb5-614d-451b-8c0b-3078b29122d8} + @@ -695,6 +698,15 @@ Debugger + + Debugger\WebSocket + + + Debugger\WebSocket + + + Debugger\WebSocket + @@ -1280,6 +1292,18 @@ Debugger + + Debugger\WebSocket + + + Debugger\WebSocket + + + Debugger\WebSocket + + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 3d9cb95a6f..5030dfcc65 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -15,113 +15,63 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include -#include -#include "json/json_writer.h" -#include "net/websocket_server.h" #include "Core/Debugger/WebSocket.h" -#include "Common/LogManager.h" +#include "Core/Debugger/WebSocket/Common.h" -// TODO: Move this to its own file? -class DebuggerLogListener : public LogListener { -public: - void Log(const LogMessage &msg) override { - std::lock_guard guard(lock_); - messages_[nextMessage_] = msg; - nextMessage_++; - if (nextMessage_ >= BUFFER_SIZE) - nextMessage_ -= BUFFER_SIZE; - count_++; - } +#include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/Debugger/WebSocket/LogBroadcaster.h" +#include "Core/Debugger/WebSocket/SteppingBroadcaster.h" - std::vector GetMessages() { - std::lock_guard guard(lock_); - int splitPoint; - int readCount; - if (read_ + BUFFER_SIZE < count_) { - // We'll start with our oldest then. - splitPoint = nextMessage_; - readCount = Count(); - } else { - splitPoint = read_; - readCount = count_ - read_; - } +// TODO: Just for now, testing... +static void WebSocketTestEvent(net::WebSocketServer *ws, const JsonGet &data) { + ws->Send(DebuggerErrorEvent("Test message", LogTypes::LNOTICE)); +} - read_ = count_; - - std::vector results; - int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE); - for (int i = splitPoint; i < splitEnd; ++i) { - results.push_back(messages_[i]); - readCount--; - } - for (int i = 0; i < readCount; ++i) { - results.push_back(messages_[i]); - } - - return results; - } - - int Count() const { - return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE; - } - -private: - enum { BUFFER_SIZE = 128 }; - LogMessage messages_[BUFFER_SIZE]; - std::mutex lock_; - int nextMessage_ = 0; - int count_ = 0; - int read_ = 0; -}; - -struct DebuggerLogEvent { - std::string header; - std::string message; - int level; - const char *channel; - - operator std::string() { - JsonWriter j; - j.begin(); - j.writeString("event", "log"); - j.writeString("header", header); - j.writeString("message", message); - j.writeInt("level", level); - j.writeString("channel", channel); - j.end(); - return j.str(); - } -}; +typedef void (*DebuggerEventHandler)(net::WebSocketServer *ws, const JsonGet &data); +static const std::unordered_map debuggerEvents({ + {"test", &WebSocketTestEvent}, +}); void HandleDebuggerRequest(const http::Request &request) { net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org"); if (!ws) return; - DebuggerLogListener *logListener = new DebuggerLogListener(); - if (LogManager::GetInstance()) - LogManager::GetInstance()->AddListener(logListener); + LogBroadcaster logger; + GameBroadcaster game; + SteppingBroadcaster stepping; - // TODO: Handle incoming messages. ws->SetTextHandler([&](const std::string &t) { - ws->Send(R"({"event":"error","message":"Bad message","level":2})"); + JsonReader reader(t.c_str(), t.size()); + if (!reader.ok()) { + ws->Send(DebuggerErrorEvent("Bad message: invalid JSON", LogTypes::LERROR)); + return; + } + + const JsonGet root = reader.root(); + const char *event = root ? root.getString("event", nullptr) : nullptr; + if (!event) { + ws->Send(DebuggerErrorEvent("Bad message: no event property", LogTypes::LERROR)); + return; + } + + auto eventFunc = debuggerEvents.find(event); + if (eventFunc != debuggerEvents.end()) { + eventFunc->second(ws, root); + } else { + ws->Send(DebuggerErrorEvent("Bad message: unknown event", LogTypes::LERROR)); + } }); ws->SetBinaryHandler([&](const std::vector &d) { - ws->Send(R"({"event":"error","message":"Bad message","level":2})"); + ws->Send(DebuggerErrorEvent("Bad message", LogTypes::LERROR)); }); - while (ws->Process(0.1f)) { - auto messages = logListener->GetMessages(); - // TODO: Check for other conditions? - for (auto msg : messages) { - ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log}); - } - continue; + while (ws->Process(1.0f / 60.0f)) { + // These send events that aren't just responses to requests. + logger.Broadcast(ws); + game.Broadcast(ws); + stepping.Broadcast(ws); } - if (LogManager::GetInstance()) - LogManager::GetInstance()->RemoveListener(logListener); - delete logListener; delete ws; } diff --git a/Core/Debugger/WebSocket/Common.h b/Core/Debugger/WebSocket/Common.h new file mode 100644 index 0000000000..ffde821f6e --- /dev/null +++ b/Core/Debugger/WebSocket/Common.h @@ -0,0 +1,42 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include "json/json_reader.h" +#include "json/json_writer.h" +#include "net/websocket_server.h" +#include "Common/Log.h" + +struct DebuggerErrorEvent { + DebuggerErrorEvent(const std::string m, LogTypes::LOG_LEVELS l) : message(m), level(l) { + } + + std::string message; + LogTypes::LOG_LEVELS level; + + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", "error"); + j.writeString("message", message); + j.writeInt("level", level); + j.end(); + return j.str(); + } +}; diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp new file mode 100644 index 0000000000..86ee9df777 --- /dev/null +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Debugger/WebSocket/Common.h" +#include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/System.h" + +void GameBroadcaster::Broadcast(net::WebSocketServer *ws) { + // TODO: This is ugly. Implement proper information instead. + // TODO: Should probably include info about which game, etc. + GlobalUIState state = GetUIState(); + if (prevState_ != state) { + if (state == UISTATE_PAUSEMENU) { + ws->Send(R"({"event":"game_pause"})"); + } else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) { + ws->Send(R"({"event":"game_resume"})"); + } else if (state == UISTATE_INGAME) { + ws->Send(R"({"event":"game_start"})"); + } else if (state == UISTATE_MENU) { + ws->Send(R"({"event":"game_quit"})"); + } + prevState_ = state; + } +} diff --git a/Core/Debugger/WebSocket/GameBroadcaster.h b/Core/Debugger/WebSocket/GameBroadcaster.h new file mode 100644 index 0000000000..63ef3eb346 --- /dev/null +++ b/Core/Debugger/WebSocket/GameBroadcaster.h @@ -0,0 +1,36 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/System.h" + +namespace net { +class WebSocketServer; +} + +struct GameBroadcaster { +public: + GameBroadcaster() { + prevState_ = GetUIState(); + } + + void Broadcast(net::WebSocketServer *ws); + +private: + GlobalUIState prevState_; +}; diff --git a/Core/Debugger/WebSocket/LogBroadcaster.cpp b/Core/Debugger/WebSocket/LogBroadcaster.cpp new file mode 100644 index 0000000000..3f23751d7c --- /dev/null +++ b/Core/Debugger/WebSocket/LogBroadcaster.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include "Common/LogManager.h" +#include "Core/Debugger/WebSocket/Common.h" +#include "Core/Debugger/WebSocket/LogBroadcaster.h" + +class DebuggerLogListener : public LogListener { +public: + void Log(const LogMessage &msg) override { + std::lock_guard guard(lock_); + messages_[nextMessage_] = msg; + nextMessage_++; + if (nextMessage_ >= BUFFER_SIZE) + nextMessage_ -= BUFFER_SIZE; + count_++; + } + + std::vector GetMessages() { + std::lock_guard guard(lock_); + int splitPoint; + int readCount; + if (read_ + BUFFER_SIZE < count_) { + // We'll start with our oldest then. + splitPoint = nextMessage_; + readCount = Count(); + } else { + splitPoint = read_; + readCount = count_ - read_; + } + + read_ = count_; + + std::vector results; + int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE); + for (int i = splitPoint; i < splitEnd; ++i) { + results.push_back(messages_[i]); + readCount--; + } + for (int i = 0; i < readCount; ++i) { + results.push_back(messages_[i]); + } + + return results; + } + + int Count() const { + return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE; + } + +private: + enum { BUFFER_SIZE = 1024 }; + LogMessage messages_[BUFFER_SIZE]; + std::mutex lock_; + int nextMessage_ = 0; + int count_ = 0; + int read_ = 0; +}; + +LogBroadcaster::LogBroadcaster() { + listener_ = new DebuggerLogListener(); + if (LogManager::GetInstance()) + LogManager::GetInstance()->AddListener(listener_); +} + +LogBroadcaster::~LogBroadcaster() { + if (LogManager::GetInstance()) + LogManager::GetInstance()->RemoveListener(listener_); + delete listener_; +} + +struct DebuggerLogEvent { + std::string header; + std::string message; + int level; + const char *channel; + + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", "log"); + j.writeString("header", header); + j.writeString("message", message); + j.writeInt("level", level); + j.writeString("channel", channel); + j.end(); + return j.str(); + } +}; + +void LogBroadcaster::Broadcast(net::WebSocketServer *ws) { + auto messages = listener_->GetMessages(); + for (auto msg : messages) { + ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log}); + } +} diff --git a/Core/Debugger/WebSocket/LogBroadcaster.h b/Core/Debugger/WebSocket/LogBroadcaster.h new file mode 100644 index 0000000000..13f26d4a06 --- /dev/null +++ b/Core/Debugger/WebSocket/LogBroadcaster.h @@ -0,0 +1,35 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +namespace net { +class WebSocketServer; +} + +class DebuggerLogListener; + +struct LogBroadcaster { +public: + LogBroadcaster(); + ~LogBroadcaster(); + + void Broadcast(net::WebSocketServer *ws); + +private: + DebuggerLogListener *listener_; +}; diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp new file mode 100644 index 0000000000..60405b4c48 --- /dev/null +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Core.h" +#include "Core/Debugger/WebSocket/Common.h" +#include "Core/Debugger/WebSocket/SteppingBroadcaster.h" +#include "Core/System.h" + +void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { + // TODO: This is somewhat primitive. It'd be nice to register a callback with Core instead? + if (coreState != prevState_) { + if (Core_IsStepping() && PSP_IsInited()) { + // TODO: Should send more data proactively. + ws->Send(R"({"event":"cpu_stepping"})"); + } + prevState_ = coreState; + } +} diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.h b/Core/Debugger/WebSocket/SteppingBroadcaster.h new file mode 100644 index 0000000000..df196d9ff8 --- /dev/null +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.h @@ -0,0 +1,36 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Core.h" + +namespace net { +class WebSocketServer; +} + +struct SteppingBroadcaster { +public: + SteppingBroadcaster() { + prevState_ = coreState; + } + + void Broadcast(net::WebSocketServer *ws); + +private: + CoreState prevState_; +}; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 2d6fa3f9f6..d21c854849 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -301,6 +301,9 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Debugger/WebSocket.cpp \ + $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ $(SRC)/Core/Dialog/PSPMsgDialog.cpp \ From 123723286d37ef8f925992404abcc39add12eda0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 14:19:24 -0700 Subject: [PATCH 164/301] Debugger: Synchronize error events using tickets. --- Core/Debugger/WebSocket.cpp | 24 +++++++++++++++++++++--- Core/Debugger/WebSocket/Common.h | 13 ++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 5030dfcc65..770772a1de 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -22,9 +22,27 @@ #include "Core/Debugger/WebSocket/LogBroadcaster.h" #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" +// This WebSocket (connected through the same port as disc sharing) allows API/debugger access to PPSSPP. +// Currently, the only subprotocol "debugger.ppsspp.org" uses a simple JSON based interface. +// +// Messages to and from PPSSPP follow the same basic format: +// { "event": "NAME", ... } +// +// And are primarily of these types: +// * Events from the debugger/client (you) to PPSSPP +// If there's a response, it will generally use the same name. It may not be immedate - it's an event. +// * Spontaneous events from PPSSPP +// Things like logs, breakpoint hits, etc. not directly requested. +// +// Otherwise you may see error events which indicate PPSSPP couldn't understand or failed internally: +// - "event": "error" +// - "message": A string describing what happened. +// - "level": Integer severity level. (1 = NOTICE, 2 = ERROR, 3 = WARN, 4 = INFO, 5 = DEBUG, 6 = VERBOSE) +// - "ticket": Optional, present if in response to an event with a "ticket" field, simply repeats that value. + // TODO: Just for now, testing... static void WebSocketTestEvent(net::WebSocketServer *ws, const JsonGet &data) { - ws->Send(DebuggerErrorEvent("Test message", LogTypes::LNOTICE)); + ws->Send(DebuggerErrorEvent("Test message", LogTypes::LNOTICE, data)); } typedef void (*DebuggerEventHandler)(net::WebSocketServer *ws, const JsonGet &data); @@ -51,7 +69,7 @@ void HandleDebuggerRequest(const http::Request &request) { const JsonGet root = reader.root(); const char *event = root ? root.getString("event", nullptr) : nullptr; if (!event) { - ws->Send(DebuggerErrorEvent("Bad message: no event property", LogTypes::LERROR)); + ws->Send(DebuggerErrorEvent("Bad message: no event property", LogTypes::LERROR, root)); return; } @@ -59,7 +77,7 @@ void HandleDebuggerRequest(const http::Request &request) { if (eventFunc != debuggerEvents.end()) { eventFunc->second(ws, root); } else { - ws->Send(DebuggerErrorEvent("Bad message: unknown event", LogTypes::LERROR)); + ws->Send(DebuggerErrorEvent("Bad message: unknown event", LogTypes::LERROR, root)); } }); ws->SetBinaryHandler([&](const std::vector &d) { diff --git a/Core/Debugger/WebSocket/Common.h b/Core/Debugger/WebSocket/Common.h index ffde821f6e..bdca223656 100644 --- a/Core/Debugger/WebSocket/Common.h +++ b/Core/Debugger/WebSocket/Common.h @@ -24,11 +24,19 @@ #include "Common/Log.h" struct DebuggerErrorEvent { - DebuggerErrorEvent(const std::string m, LogTypes::LOG_LEVELS l) : message(m), level(l) { + DebuggerErrorEvent(const std::string m, LogTypes::LOG_LEVELS l, const JsonGet data = JsonValue(JSON_NULL)) + : message(m), level(l) { + // Need to format right away, before it's out of scope. + if (data) { + const JsonNode *value = data.get("ticket"); + if (value) + ticketRaw = json_stringify(value); + } } std::string message; LogTypes::LOG_LEVELS level; + std::string ticketRaw; operator std::string() { JsonWriter j; @@ -36,6 +44,9 @@ struct DebuggerErrorEvent { j.writeString("event", "error"); j.writeString("message", message); j.writeInt("level", level); + if (!ticketRaw.empty()) { + j.writeRaw("ticket", ticketRaw); + } j.end(); return j.str(); } From 98cddad73a1befc9f962ba1d773345730c321c82 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 08:49:33 -0700 Subject: [PATCH 165/301] Debugger: Minor cleanup in MIPSDebugInterface. --- Core/MIPS/MIPSDebugInterface.h | 51 ++++++++++++---------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 31cbdde414..088256e802 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -54,85 +54,70 @@ public: u32 GetLR() override { return cpu->r[MIPS_REG_RA]; } void SetPC(u32 _pc) override { cpu->pc = _pc; } - const char *GetCategoryName(int cat) override - { - const char *names[3] = {("GPR"),("FPU"),("VFPU")}; + const char *GetCategoryName(int cat) override { + static const char *const names[3] = { "GPR", "FPU", "VFPU" }; return names[cat]; } int GetNumCategories() override { return 3; } - int GetNumRegsInCategory(int cat) override - { - int r[3] = {32,32,32}; + int GetNumRegsInCategory(int cat) override { + static int r[3] = { 32, 32, 128 }; return r[cat]; } const char *GetRegName(int cat, int index) override; - void PrintRegValue(int cat, int index, char *out) override - { - switch (cat) - { + void PrintRegValue(int cat, int index, char *out) override { + switch (cat) { case 0: sprintf(out, "%08X", cpu->r[index]); break; case 1: sprintf(out, "%f", cpu->f[index]); break; case 2: sprintf(out, "N/A"); break; } } - u32 GetHi() override - { + u32 GetHi() override { return cpu->hi; } - u32 GetLo() override - { + u32 GetLo() override { return cpu->lo; } - void SetHi(u32 val) override - { + void SetHi(u32 val) override { cpu->hi = val; } - void SetLo(u32 val) override - { + void SetLo(u32 val) override { cpu->lo = val; } - u32 GetRegValue(int cat, int index) override - { - u32 temp; - switch (cat) - { + u32 GetRegValue(int cat, int index) override { + switch (cat) { case 0: return cpu->r[index]; case 1: - memcpy(&temp, &cpu->f[index], 4); - return temp; + return cpu->fi[index]; case 2: - memcpy(&temp, &cpu->v[voffset[index]], 4); - return temp; + return cpu->vi[voffset[index]]; default: return 0; } } - void SetRegValue(int cat, int index, u32 value) override - { - switch (cat) - { + void SetRegValue(int cat, int index, u32 value) override { + switch (cat) { case 0: if (index != 0) cpu->r[index] = value; break; case 1: - memcpy(&cpu->f[index], &value, 4); + cpu->fi[index] = value; break; case 2: - memcpy(&cpu->v[voffset[index]], &value, 4); + cpu->vi[voffset[index]] = value; break; default: From 1f987bf1c177e7dd5e30d4dfde7da5be5e53e0d8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 08:50:25 -0700 Subject: [PATCH 166/301] Debugger: Initial register get/set APIs. --- CMakeLists.txt | 5 +- Core/Core.vcxproj | 5 +- Core/Core.vcxproj.filters | 11 +- Core/Debugger/WebSocket.cpp | 28 ++- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 224 ++++++++++++++++++ Core/Debugger/WebSocket/CPUCoreSubscriber.h | 24 ++ Core/Debugger/WebSocket/GameBroadcaster.cpp | 2 +- Core/Debugger/WebSocket/LogBroadcaster.cpp | 2 +- .../WebSocket/SteppingBroadcaster.cpp | 2 +- Core/Debugger/WebSocket/WebSocketUtils.cpp | 106 +++++++++ .../WebSocket/{Common.h => WebSocketUtils.h} | 33 +++ android/jni/Android.mk | 2 + 12 files changed, 425 insertions(+), 19 deletions(-) create mode 100644 Core/Debugger/WebSocket/CPUCoreSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/CPUCoreSubscriber.h create mode 100644 Core/Debugger/WebSocket/WebSocketUtils.cpp rename Core/Debugger/WebSocket/{Common.h => WebSocketUtils.h} (66%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2265f2b5de..9ea61d912b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1404,13 +1404,16 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/DisassemblyManager.h Core/Debugger/WebSocket.cpp Core/Debugger/WebSocket.h - Core/Debugger/WebSocket/Common.h + Core/Debugger/WebSocket/CPUCoreSubscriber.cpp + Core/Debugger/WebSocket/CPUCoreSubscriber.h Core/Debugger/WebSocket/GameBroadcaster.cpp Core/Debugger/WebSocket/GameBroadcaster.h Core/Debugger/WebSocket/LogBroadcaster.cpp Core/Debugger/WebSocket/LogBroadcaster.h Core/Debugger/WebSocket/SteppingBroadcaster.cpp Core/Debugger/WebSocket/SteppingBroadcaster.h + Core/Debugger/WebSocket/WebSocketUtils.cpp + Core/Debugger/WebSocket/WebSocketUtils.h Core/Dialog/PSPDialog.cpp Core/Dialog/PSPDialog.h Core/Dialog/PSPGamedataInstallDialog.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index e713cd5057..c102fd2cb7 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -185,9 +185,11 @@ + + @@ -537,7 +539,8 @@ - + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 3449fa0158..3a8a72a82e 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -707,6 +707,12 @@ Debugger\WebSocket + + Debugger\WebSocket + + + Debugger\WebSocket + @@ -1301,7 +1307,10 @@ Debugger\WebSocket - + + Debugger\WebSocket + + Debugger\WebSocket diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 770772a1de..2e3e540843 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -16,11 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Core/Debugger/WebSocket.h" -#include "Core/Debugger/WebSocket/Common.h" - -#include "Core/Debugger/WebSocket/GameBroadcaster.h" -#include "Core/Debugger/WebSocket/LogBroadcaster.h" -#include "Core/Debugger/WebSocket/SteppingBroadcaster.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" // This WebSocket (connected through the same port as disc sharing) allows API/debugger access to PPSSPP. // Currently, the only subprotocol "debugger.ppsspp.org" uses a simple JSON based interface. @@ -40,14 +36,17 @@ // - "level": Integer severity level. (1 = NOTICE, 2 = ERROR, 3 = WARN, 4 = INFO, 5 = DEBUG, 6 = VERBOSE) // - "ticket": Optional, present if in response to an event with a "ticket" field, simply repeats that value. -// TODO: Just for now, testing... -static void WebSocketTestEvent(net::WebSocketServer *ws, const JsonGet &data) { - ws->Send(DebuggerErrorEvent("Test message", LogTypes::LNOTICE, data)); -} +#include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/Debugger/WebSocket/LogBroadcaster.h" +#include "Core/Debugger/WebSocket/SteppingBroadcaster.h" -typedef void (*DebuggerEventHandler)(net::WebSocketServer *ws, const JsonGet &data); +#include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" + +typedef void (*DebuggerEventHandler)(DebuggerRequest &req); static const std::unordered_map debuggerEvents({ - {"test", &WebSocketTestEvent}, + {"cpu.getAllRegs", &WebSocketCPUGetAllRegs}, + {"cpu.getReg", &WebSocketCPUGetReg}, + {"cpu.setReg", &WebSocketCPUSetReg}, }); void HandleDebuggerRequest(const http::Request &request) { @@ -73,11 +72,14 @@ void HandleDebuggerRequest(const http::Request &request) { return; } + DebuggerRequest req(event, ws, root); + auto eventFunc = debuggerEvents.find(event); if (eventFunc != debuggerEvents.end()) { - eventFunc->second(ws, root); + eventFunc->second(req); + req.Finish(); } else { - ws->Send(DebuggerErrorEvent("Bad message: unknown event", LogTypes::LERROR, root)); + req.Fail("Bad message: unknown event"); } }); ws->SetBinaryHandler([&](const std::vector &d) { diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp new file mode 100644 index 0000000000..c3ef6e6ff5 --- /dev/null +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -0,0 +1,224 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/StringUtils.h" +#include "Core/Core.h" +#include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSDebugInterface.h" + +static std::string RegValueAsFloat(uint32_t u) { + union { + uint32_t u; + float f; + } bits = { u }; + return StringFromFormat("%f", bits.f); +} + +void WebSocketCPUGetAllRegs(DebuggerRequest &req) { + JsonWriter &json = req.Respond(); + + json.pushArray("categories"); + for (int c = 0; c < currentDebugMIPS->GetNumCategories(); ++c) { + json.pushDict(); + json.writeInt("id", c); + json.writeString("name", currentDebugMIPS->GetCategoryName(c)); + + int total = currentDebugMIPS->GetNumRegsInCategory(c); + + json.pushArray("names"); + for (int r = 0; r < total; ++r) + json.writeString(currentDebugMIPS->GetRegName(c, r)); + if (c == 0) { + json.writeString("pc"); + json.writeString("hi"); + json.writeString("lo"); + } + json.pop(); + + json.pushArray("intValues"); + // Writing as floating point to avoid negatives. Actually double, so safe. + for (int r = 0; r < total; ++r) + json.writeFloat(currentDebugMIPS->GetRegValue(c, r)); + if (c == 0) { + json.writeFloat(currentDebugMIPS->GetPC()); + json.writeFloat(currentDebugMIPS->GetHi()); + json.writeFloat(currentDebugMIPS->GetLo()); + } + json.pop(); + + json.pushArray("floatValues"); + // Note: String so it can have Infinity and NaN. + for (int r = 0; r < total; ++r) + json.writeString(RegValueAsFloat(currentDebugMIPS->GetRegValue(c, r))); + if (c == 0) { + json.writeString(RegValueAsFloat(currentDebugMIPS->GetPC())); + json.writeString(RegValueAsFloat(currentDebugMIPS->GetHi())); + json.writeString(RegValueAsFloat(currentDebugMIPS->GetLo())); + } + json.pop(); + + json.pop(); + } + json.pop(); +} + +enum class DebuggerRegType { + INVALID, + NORMAL, + PC, + HI, + LO, +}; + +static DebuggerRegType ValidateRegName(DebuggerRequest &req, const std::string &name, int *cat, int *reg) { + if (name == "pc") { + *cat = 0; + *reg = 32; + return DebuggerRegType::PC; + } + if (name == "hi") { + *cat = 0; + *reg = 33; + return DebuggerRegType::HI; + } + if (name == "lo") { + *cat = 0; + *reg = 34; + return DebuggerRegType::LO; + } + + for (int c = 0; c < currentDebugMIPS->GetNumCategories(); ++c) { + int total = currentDebugMIPS->GetNumRegsInCategory(c); + for (int r = 0; r < total; ++r) { + if (name == currentDebugMIPS->GetRegName(c, r)) { + *cat = c; + *reg = r; + return DebuggerRegType::NORMAL; + } + } + } + + req.Fail("Invalid 'name' parameter"); + return DebuggerRegType::INVALID; +} + +static DebuggerRegType ValidateCatReg(DebuggerRequest &req, int *cat, int *reg) { + const char *name = req.data.getString("name", nullptr); + if (name) + return ValidateRegName(req, name, cat, reg); + + *cat = req.data.getInt("category", -1); + *reg = req.data.getInt("register", -1); + + if (*cat < 0 || *cat >= currentDebugMIPS->GetNumCategories()) { + req.Fail("Invalid 'category' parameter"); + return DebuggerRegType::INVALID; + } + + // TODO: We fake it for GPR... not sure yet if this is a good thing. + if (*cat == 0) { + // Intentionally retains the reg value. + if (*reg == 32) + return DebuggerRegType::PC; + if (*reg == 33) + return DebuggerRegType::HI; + if (*reg == 34) + return DebuggerRegType::LO; + } + + if (*reg < 0 || *reg >= currentDebugMIPS->GetNumRegsInCategory(*cat)) { + req.Fail("Invalid 'register' parameter"); + return DebuggerRegType::INVALID; + } + + return DebuggerRegType::NORMAL; +} + +void WebSocketCPUGetReg(DebuggerRequest &req) { + int cat, reg; + uint32_t val; + switch (ValidateCatReg(req, &cat, ®)) { + case DebuggerRegType::NORMAL: + val = currentDebugMIPS->GetRegValue(cat, reg); + break; + + case DebuggerRegType::PC: + val = currentDebugMIPS->GetPC(); + break; + case DebuggerRegType::HI: + val = currentDebugMIPS->GetHi(); + break; + case DebuggerRegType::LO: + val = currentDebugMIPS->GetLo(); + break; + + case DebuggerRegType::INVALID: + // Error response already sent. + return; + } + + JsonWriter &json = req.Respond(); + json.writeInt("category", cat); + json.writeInt("register", reg); + json.writeFloat("intValue", val); + json.writeString("floatValue", RegValueAsFloat(val)); +} + +void WebSocketCPUSetReg(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + if (!Core_IsStepping()) { + return req.Fail("CPU currently running (cpu.interrupt first)"); + } + + uint32_t val; + if (!req.ParamU32OrFloatBits("value", &val)) { + // Already sent error. + return; + } + + int cat, reg; + switch (ValidateCatReg(req, &cat, ®)) { + case DebuggerRegType::NORMAL: + currentDebugMIPS->SetRegValue(cat, reg, val); + break; + + case DebuggerRegType::PC: + currentDebugMIPS->SetPC(val); + break; + case DebuggerRegType::HI: + currentDebugMIPS->SetHi(val); + break; + case DebuggerRegType::LO: + currentDebugMIPS->SetLo(val); + break; + + case DebuggerRegType::INVALID: + // Error response already sent. + return; + } + + JsonWriter &json = req.Respond(); + // Repeat it back just to avoid confusion on how it parsed. + json.writeInt("category", cat); + json.writeInt("register", reg); + json.writeFloat("intValue", val); + json.writeString("floatValue", RegValueAsFloat(val)); +} diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h new file mode 100644 index 0000000000..e24b16398f --- /dev/null +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -0,0 +1,24 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +struct DebuggerRequest; + +void WebSocketCPUGetAllRegs(DebuggerRequest &req); +void WebSocketCPUGetReg(DebuggerRequest &req); +void WebSocketCPUSetReg(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp index 86ee9df777..e1a9d2ae4e 100644 --- a/Core/Debugger/WebSocket/GameBroadcaster.cpp +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -15,8 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Core/Debugger/WebSocket/Common.h" #include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/System.h" void GameBroadcaster::Broadcast(net::WebSocketServer *ws) { diff --git a/Core/Debugger/WebSocket/LogBroadcaster.cpp b/Core/Debugger/WebSocket/LogBroadcaster.cpp index 3f23751d7c..2474294e0a 100644 --- a/Core/Debugger/WebSocket/LogBroadcaster.cpp +++ b/Core/Debugger/WebSocket/LogBroadcaster.cpp @@ -18,8 +18,8 @@ #include #include #include "Common/LogManager.h" -#include "Core/Debugger/WebSocket/Common.h" #include "Core/Debugger/WebSocket/LogBroadcaster.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" class DebuggerLogListener : public LogListener { public: diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp index 60405b4c48..ca44bc06b4 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -16,8 +16,8 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Core/Core.h" -#include "Core/Debugger/WebSocket/Common.h" #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/System.h" void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp new file mode 100644 index 0000000000..3293e5da22 --- /dev/null +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/StringUtils.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +JsonWriter &DebuggerRequest::Respond() { + writer_.begin(); + writer_.writeString("event", name); + DebuggerJsonAddTicket(writer_, data); + + responseBegun_ = true; + return writer_; +} + +void DebuggerRequest::Finish() { + if (responseBegun_ && !responseSent_) { + writer_.end(); + ws->Send(writer_.str()); + responseBegun_ = false; + responseSent_ = true; + } +} + +static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) { + if (TryParse(str, out)) + return true; + + // Now let's try signed (the above parses only positive.) + if (str[0] == '-' && TryParse(&str[1], out)) { + *out = static_cast(-static_cast(*out)); + return true; + } + + // We have to try float last because we use float bits. + union { + uint32_t u; + float f; + } bits; + if (allowFloat && TryParse(str, &bits.f)) { + *out = bits.u; + return true; + } + + return false; +} + +bool DebuggerRequest::ParamU32(const char *name, uint32_t *out) { + const JsonNode *node = data.get(name); + if (!node) { + Fail(StringFromFormat("Missing '%s' parameter", name)); + return false; + } + + // TODO: For now, only supporting strings. Switch to gason? + // Otherwise we get overflow (signed integer parsing.) + if (node->value.getTag() != JSON_STRING) { + Fail(StringFromFormat("Invalid '%s' parameter type", name)); + return false; + } + + if (U32FromString(node->value.toString(), out, false)) + return true; + + Fail(StringFromFormat("Could not parse '%s' parameter", name)); + return false; +} + +bool DebuggerRequest::ParamU32OrFloatBits(const char *name, uint32_t *out) { + const JsonNode *node = data.get(name); + if (!node) { + Fail(StringFromFormat("Missing '%s' parameter", name)); + return false; + } + + // TODO: For now, only supporting strings and floats. Switch to gason? + // Otherwise we get overflow (signed integer parsing.) + if (node->value.getTag() == JSON_NUMBER) { + Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range (use string for float)", name)); + return false; + } + if (node->value.getTag() != JSON_STRING) { + Fail(StringFromFormat("Invalid '%s' parameter type", name)); + return false; + } + + if (U32FromString(node->value.toString(), out, true)) + return true; + + Fail(StringFromFormat("Could not parse '%s' parameter", name)); + return false; +} diff --git a/Core/Debugger/WebSocket/Common.h b/Core/Debugger/WebSocket/WebSocketUtils.h similarity index 66% rename from Core/Debugger/WebSocket/Common.h rename to Core/Debugger/WebSocket/WebSocketUtils.h index bdca223656..19894939bb 100644 --- a/Core/Debugger/WebSocket/Common.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -17,12 +17,19 @@ #pragma once +#include #include #include "json/json_reader.h" #include "json/json_writer.h" #include "net/websocket_server.h" #include "Common/Log.h" +static inline void DebuggerJsonAddTicket(JsonWriter &writer, const JsonGet &data) { + const JsonNode *value = data.get("ticket"); + if (value) + writer.writeRaw("ticket", json_stringify(value)); +} + struct DebuggerErrorEvent { DebuggerErrorEvent(const std::string m, LogTypes::LOG_LEVELS l, const JsonGet data = JsonValue(JSON_NULL)) : message(m), level(l) { @@ -51,3 +58,29 @@ struct DebuggerErrorEvent { return j.str(); } }; + +struct DebuggerRequest { + DebuggerRequest(const char *n, net::WebSocketServer *w, const JsonGet &d) + : name(n), ws(w), data(d) { + } + + const char *name; + net::WebSocketServer *ws; + const JsonGet data; + + void Fail(const std::string &message) { + ws->Send(DebuggerErrorEvent(message, LogTypes::LERROR, data)); + responseSent_ = true; + } + + bool ParamU32(const char *name, uint32_t *out); + bool ParamU32OrFloatBits(const char *name, uint32_t *out); + + JsonWriter &Respond(); + void Finish(); + +private: + JsonWriter writer_; + bool responseBegun_ = false; + bool responseSent_ = false; +}; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index d21c854849..b5b8dda1d2 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -301,9 +301,11 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Debugger/WebSocket.cpp \ + $(SRC)/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ $(SRC)/Core/Dialog/PSPMsgDialog.cpp \ From be3b50dd74f18c55745994471fe4e487058fdf27 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 11:24:10 -0700 Subject: [PATCH 167/301] Debugger: Improve JSON number parsing. With gason, this becomes doable without overflow issues. --- Core/Debugger/WebSocket.cpp | 1 - Core/Debugger/WebSocket/WebSocketUtils.cpp | 49 +++++++++++++++++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 2e3e540843..3615bc4c27 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -73,7 +73,6 @@ void HandleDebuggerRequest(const http::Request &request) { } DebuggerRequest req(event, ws, root); - auto eventFunc = debuggerEvents.find(event); if (eventFunc != debuggerEvents.end()) { eventFunc->second(req); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index 3293e5da22..b698182148 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include #include "Common/StringUtils.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" @@ -66,8 +68,26 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out) { return false; } - // TODO: For now, only supporting strings. Switch to gason? - // Otherwise we get overflow (signed integer parsing.) + if (node->value.getTag() == JSON_NUMBER) { + double val = node->value.toNumber(); + bool isInteger = trunc(val) == val; + if (!isInteger) { + Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name)); + return false; + } + + if (val < 0 && val >= std::numeric_limits::min()) { + // Convert to unsigned representation. + *out = (uint32_t)(int32_t)val; + return true; + } else if (val >= 0 && val <= std::numeric_limits::max()) { + *out = (uint32_t)val; + return true; + } + + Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range", name)); + return false; + } if (node->value.getTag() != JSON_STRING) { Fail(StringFromFormat("Invalid '%s' parameter type", name)); return false; @@ -76,7 +96,7 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out) { if (U32FromString(node->value.toString(), out, false)) return true; - Fail(StringFromFormat("Could not parse '%s' parameter", name)); + Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name)); return false; } @@ -87,9 +107,26 @@ bool DebuggerRequest::ParamU32OrFloatBits(const char *name, uint32_t *out) { return false; } - // TODO: For now, only supporting strings and floats. Switch to gason? - // Otherwise we get overflow (signed integer parsing.) if (node->value.getTag() == JSON_NUMBER) { + double val = node->value.toNumber(); + bool isInteger = trunc(val) == val; + + // JSON doesn't give a great way to differentiate ints and floats. + // Let's play it safe and require a string. + if (!isInteger) { + Fail(StringFromFormat("Could not parse '%s' parameter: use a string for non integer values", name)); + return false; + } + + if (val < 0 && val >= std::numeric_limits::min()) { + // Convert to unsigned representation. + *out = (uint32_t)(int32_t)val; + return true; + } else if (val >= 0 && val <= std::numeric_limits::max()) { + *out = (uint32_t)val; + return true; + } + Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range (use string for float)", name)); return false; } @@ -101,6 +138,6 @@ bool DebuggerRequest::ParamU32OrFloatBits(const char *name, uint32_t *out) { if (U32FromString(node->value.toString(), out, true)) return true; - Fail(StringFromFormat("Could not parse '%s' parameter", name)); + Fail(StringFromFormat("Could not parse '%s' parameter: number expected", name)); return false; } From 3c458310edd6c6df47dfb85713ebc4556d53e2a0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 11:34:57 -0700 Subject: [PATCH 168/301] Debugger: Allow setting regs to inf/nan. --- Core/Debugger/WebSocket/WebSocketUtils.cpp | 29 ++++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index b698182148..8d75122018 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -48,14 +48,27 @@ static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) { return true; } - // We have to try float last because we use float bits. - union { - uint32_t u; - float f; - } bits; - if (allowFloat && TryParse(str, &bits.f)) { - *out = bits.u; - return true; + // We have to try float last because we use float bits, so 1.0 != 1. + if (allowFloat) { + union { + uint32_t u; + float f; + } bits; + if (TryParse(str, &bits.f)) { + *out = bits.u; + return true; + } + + if (!strcasecmp(str, "nan")) { + *out = 0x7FC00000; + return true; + } else if (!strcasecmp(str, "infinity") || !strcasecmp(str, "inf")) { + *out = 0x7F800000; + return true; + } else if (!strcasecmp(str, "-infinity") || !strcasecmp(str, "-inf")) { + *out = 0xFF800000; + return true; + } } return false; From 0a21063525965d43a556c1da18995d97c7fb66ed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 11:51:19 -0700 Subject: [PATCH 169/301] Debugger: Cleanup, add initial API docs. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 55 +++++++++++++++++-- Core/Debugger/WebSocket/GameBroadcaster.cpp | 8 +-- .../WebSocket/SteppingBroadcaster.cpp | 2 +- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index c3ef6e6ff5..f2ef93b75e 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -30,6 +30,17 @@ static std::string RegValueAsFloat(uint32_t u) { return StringFromFormat("%f", bits.f); } +// Retrieve all regs and their values (cpu.getAllRegs) +// +// No parameters. +// +// Response (same event name): +// - categories: array of objects: +// - id: "category" property to use for other events. +// - name: a category name, such as "GPR". +// - registerNames: array of string names of the registers (size varies per category.) +// - uintValues: array of unsigned integer values for the registers. +// - floatValues: array of strings showing float representation. May be "nan", "inf", or "-inf". void WebSocketCPUGetAllRegs(DebuggerRequest &req) { JsonWriter &json = req.Respond(); @@ -41,7 +52,7 @@ void WebSocketCPUGetAllRegs(DebuggerRequest &req) { int total = currentDebugMIPS->GetNumRegsInCategory(c); - json.pushArray("names"); + json.pushArray("registerNames"); for (int r = 0; r < total; ++r) json.writeString(currentDebugMIPS->GetRegName(c, r)); if (c == 0) { @@ -51,7 +62,7 @@ void WebSocketCPUGetAllRegs(DebuggerRequest &req) { } json.pop(); - json.pushArray("intValues"); + json.pushArray("uintValues"); // Writing as floating point to avoid negatives. Actually double, so safe. for (int r = 0; r < total; ++r) json.writeFloat(currentDebugMIPS->GetRegValue(c, r)); @@ -150,6 +161,20 @@ static DebuggerRegType ValidateCatReg(DebuggerRequest &req, int *cat, int *reg) return DebuggerRegType::NORMAL; } +// Retrieve the value of a single register (cpu.getReg) +// +// Parameters (by name): +// - name: string name of register to lookup. +// +// Parameters (by category id and index, ignored if name specified): +// - category: id of category for the register. +// - register: index into array of registers. +// +// Response (same event name): +// - category: id of category for the register. +// - register: index into array of registers. +// - uintValue: value in register. +// - floatValue: string showing float representation. May be "nan", "inf", or "-inf". void WebSocketCPUGetReg(DebuggerRequest &req) { int cat, reg; uint32_t val; @@ -176,16 +201,36 @@ void WebSocketCPUGetReg(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeInt("category", cat); json.writeInt("register", reg); - json.writeFloat("intValue", val); + json.writeFloat("uintValue", val); json.writeString("floatValue", RegValueAsFloat(val)); } +// Retrieve the value of a single register (cpu.getReg) +// +// Parameters (by name): +// - name: string name of register to lookup. +// - value: number (uint values only) or string to set to. Values may include +// "0x1234", "1.5", "nan", "-inf", etc. For a float, use a string with decimal e.g. "1.0". +// +// Parameters (by category id and index, ignored if name specified): +// - category: id of category for the register. +// - register: index into array of registers. +// - value: number (uint values only) or string to set to. Values may include +// "0x1234", "1.5", "nan", "-inf", etc. For a float, use a string with decimal e.g. "1.0". +// +// Response (same event name): +// - category: id of category for the register. +// - register: index into array of registers. +// - uintValue: new value in register. +// - floatValue: string showing float representation. May be "nan", "inf", or "-inf". +// +// NOTE: Cannot be called unless the CPU is currently stepping. void WebSocketCPUSetReg(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); } if (!Core_IsStepping()) { - return req.Fail("CPU currently running (cpu.interrupt first)"); + return req.Fail("CPU currently running (cpu.stepping first)"); } uint32_t val; @@ -219,6 +264,6 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { // Repeat it back just to avoid confusion on how it parsed. json.writeInt("category", cat); json.writeInt("register", reg); - json.writeFloat("intValue", val); + json.writeFloat("uintValue", val); json.writeString("floatValue", RegValueAsFloat(val)); } diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp index e1a9d2ae4e..37a9e2c919 100644 --- a/Core/Debugger/WebSocket/GameBroadcaster.cpp +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -25,13 +25,13 @@ void GameBroadcaster::Broadcast(net::WebSocketServer *ws) { GlobalUIState state = GetUIState(); if (prevState_ != state) { if (state == UISTATE_PAUSEMENU) { - ws->Send(R"({"event":"game_pause"})"); + ws->Send(R"({"event":"game.pause"})"); } else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) { - ws->Send(R"({"event":"game_resume"})"); + ws->Send(R"({"event":"game.resume"})"); } else if (state == UISTATE_INGAME) { - ws->Send(R"({"event":"game_start"})"); + ws->Send(R"({"event":"game.start"})"); } else if (state == UISTATE_MENU) { - ws->Send(R"({"event":"game_quit"})"); + ws->Send(R"({"event":"game.quit"})"); } prevState_ = state; } diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp index ca44bc06b4..7be39bd798 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -25,7 +25,7 @@ void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { if (coreState != prevState_) { if (Core_IsStepping() && PSP_IsInited()) { // TODO: Should send more data proactively. - ws->Send(R"({"event":"cpu_stepping"})"); + ws->Send(R"({"event":"cpu.stepping"})"); } prevState_ = coreState; } From a4044fd6a01e34abc6ef45b6d49c638a129fd445 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 15:52:00 -0700 Subject: [PATCH 170/301] Debugger: Reorganize state handling. Looking like this will be needed for disasm caches, etc. --- Core/Debugger/WebSocket.cpp | 32 +++++++++++++++---- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 9 ++++++ Core/Debugger/WebSocket/CPUCoreSubscriber.h | 4 ++- Core/Debugger/WebSocket/WebSocketUtils.h | 3 ++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 3615bc4c27..9d703f19f1 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -42,11 +42,15 @@ #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" -typedef void (*DebuggerEventHandler)(DebuggerRequest &req); -static const std::unordered_map debuggerEvents({ - {"cpu.getAllRegs", &WebSocketCPUGetAllRegs}, - {"cpu.getReg", &WebSocketCPUGetReg}, - {"cpu.setReg", &WebSocketCPUSetReg}, +typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); +typedef void (*Subscribershutdown)(void *p); +struct SubscriberInfo { + SubscriberInit init; + Subscribershutdown shutdown; +}; + +static const std::vector subscribers({ + { &WebSocketCPUCoreInit, nullptr }, }); void HandleDebuggerRequest(const http::Request &request) { @@ -58,6 +62,12 @@ void HandleDebuggerRequest(const http::Request &request) { GameBroadcaster game; SteppingBroadcaster stepping; + std::unordered_map eventHandlers; + std::vector subscriberData; + for (auto info : subscribers) { + subscriberData.push_back(info.init(eventHandlers)); + } + ws->SetTextHandler([&](const std::string &t) { JsonReader reader(t.c_str(), t.size()); if (!reader.ok()) { @@ -73,8 +83,8 @@ void HandleDebuggerRequest(const http::Request &request) { } DebuggerRequest req(event, ws, root); - auto eventFunc = debuggerEvents.find(event); - if (eventFunc != debuggerEvents.end()) { + auto eventFunc = eventHandlers.find(event); + if (eventFunc != eventHandlers.end()) { eventFunc->second(req); req.Finish(); } else { @@ -92,5 +102,13 @@ void HandleDebuggerRequest(const http::Request &request) { stepping.Broadcast(ws); } + for (size_t i = 0; i < subscribers.size(); ++i) { + if (subscribers[i].shutdown) { + subscribers[i].shutdown(subscriberData[i]); + } else { + assert(!subscriberData[i]); + } + } + delete ws; } diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index f2ef93b75e..4e5fab8a59 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -22,6 +22,15 @@ #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSDebugInterface.h" +void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) { + // No need to bind or alloc state, these are all global. + map["cpu.getAllRegs"] = &WebSocketCPUGetAllRegs; + map["cpu.getReg"] = &WebSocketCPUGetReg; + map["cpu.setReg"] = &WebSocketCPUSetReg; + + return nullptr; +} + static std::string RegValueAsFloat(uint32_t u) { union { uint32_t u; diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h index e24b16398f..78c3173174 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -17,7 +17,9 @@ #pragma once -struct DebuggerRequest; +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map); void WebSocketCPUGetAllRegs(DebuggerRequest &req); void WebSocketCPUGetReg(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 19894939bb..6e4e62d36c 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -84,3 +84,6 @@ private: bool responseBegun_ = false; bool responseSent_ = false; }; + +typedef std::function DebuggerEventHandler; +typedef std::unordered_map DebuggerEventHandlerMap; From a3419946222624b0addc5b3296643460645d4d6b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 15:53:36 -0700 Subject: [PATCH 171/301] Debugger: Add cpu.stepping and cpu.resume. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 32 +++++++++++++++++++ Core/Debugger/WebSocket/CPUCoreSubscriber.h | 2 ++ .../WebSocket/SteppingBroadcaster.cpp | 9 ++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 4e5fab8a59..8f7fd07e78 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -24,6 +24,8 @@ void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) { // No need to bind or alloc state, these are all global. + map["cpu.stepping"] = &WebSocketCPUStepping; + map["cpu.resume"] = &WebSocketCPUResume; map["cpu.getAllRegs"] = &WebSocketCPUGetAllRegs; map["cpu.getReg"] = &WebSocketCPUGetReg; map["cpu.setReg"] = &WebSocketCPUSetReg; @@ -39,6 +41,36 @@ static std::string RegValueAsFloat(uint32_t u) { return StringFromFormat("%f", bits.f); } +// Begin stepping and pause the CPU (cpu.stepping) +// +// No parameters. +// +// No immediate response. Once CPU is stepping, a "cpu.stepping" event will be sent. +void WebSocketCPUStepping(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + if (!Core_IsStepping() && Core_IsActive()) { + Core_EnableStepping(true); + } +} + +// Stop stepping and resume the CPU (cpu.resume) +// +// No parameters. +// +// No immediate response. Once CPU is stepping, a "cpu.resume" event will be sent. +void WebSocketCPUResume(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + if (!Core_IsStepping() || coreState == CORE_POWERDOWN) { + return req.Fail("CPU not stepping"); + } + + Core_EnableStepping(false); +} + // Retrieve all regs and their values (cpu.getAllRegs) // // No parameters. diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h index 78c3173174..4998d9af0b 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -21,6 +21,8 @@ void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map); +void WebSocketCPUStepping(DebuggerRequest &req); +void WebSocketCPUResume(DebuggerRequest &req); void WebSocketCPUGetAllRegs(DebuggerRequest &req); void WebSocketCPUGetReg(DebuggerRequest &req); void WebSocketCPUSetReg(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp index 7be39bd798..89e91bc63d 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -22,11 +22,14 @@ void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { // TODO: This is somewhat primitive. It'd be nice to register a callback with Core instead? - if (coreState != prevState_) { - if (Core_IsStepping() && PSP_IsInited()) { + if (coreState != prevState_ && PSP_IsInited()) { + // We ignore CORE_POWERDOWN. + if (coreState == CORE_STEPPING) { // TODO: Should send more data proactively. ws->Send(R"({"event":"cpu.stepping"})"); + } else if (prevState_ == CORE_STEPPING && Core_IsActive()) { + ws->Send(R"({"event":"cpu.resume"})"); } - prevState_ = coreState; } + prevState_ = coreState; } From c44d8dbe2d0d318b8a99548bfb3f1dffdd61322e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 16:31:00 -0700 Subject: [PATCH 172/301] Debugger: Name the WebSocket debugger thread. --- Core/Debugger/WebSocket.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 9d703f19f1..6343a8331d 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "thread/threadutil.h" #include "Core/Debugger/WebSocket.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" @@ -58,6 +59,8 @@ void HandleDebuggerRequest(const http::Request &request) { if (!ws) return; + setCurrentThreadName("Debugger"); + LogBroadcaster logger; GameBroadcaster game; SteppingBroadcaster stepping; From d67a1d4a3aac40f31a40b4d5aa6599ca7367040b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 19 Apr 2018 20:59:20 -0700 Subject: [PATCH 173/301] Debugger: Allow pinging current status. Helps especially when reconnecting. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 19 +++++++++++++++++++ Core/Debugger/WebSocket/CPUCoreSubscriber.h | 1 + 2 files changed, 20 insertions(+) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 8f7fd07e78..2453675a33 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -26,6 +26,7 @@ void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) { // No need to bind or alloc state, these are all global. map["cpu.stepping"] = &WebSocketCPUStepping; map["cpu.resume"] = &WebSocketCPUResume; + map["cpu.status"] = &WebSocketCPUStatus; map["cpu.getAllRegs"] = &WebSocketCPUGetAllRegs; map["cpu.getReg"] = &WebSocketCPUGetReg; map["cpu.setReg"] = &WebSocketCPUSetReg; @@ -71,6 +72,19 @@ void WebSocketCPUResume(DebuggerRequest &req) { Core_EnableStepping(false); } +// Request the current CPU status (cpu.status) +// +// No parameters. +// +// Response (same event name): +// - stepping: boolean, CPU currently stepping. +// - paused: boolean, CPU paused or not started yet. +void WebSocketCPUStatus(DebuggerRequest &req) { + JsonWriter &json = req.Respond(); + json.writeBool("stepping", PSP_IsInited() && Core_IsStepping() && coreState != CORE_POWERDOWN); + json.writeBool("paused", GetUIState() != UISTATE_INGAME); +} + // Retrieve all regs and their values (cpu.getAllRegs) // // No parameters. @@ -283,7 +297,12 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { int cat, reg; switch (ValidateCatReg(req, &cat, ®)) { case DebuggerRegType::NORMAL: + if (cat == 0 && reg == 0 && val != 0) { + return req.Fail("Cannot change reg zero"); + } currentDebugMIPS->SetRegValue(cat, reg, val); + // In case part of it was ignored (e.g. flags reg.) + val = currentDebugMIPS->GetRegValue(cat, reg); break; case DebuggerRegType::PC: diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h index 4998d9af0b..effff73a76 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -23,6 +23,7 @@ void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map); void WebSocketCPUStepping(DebuggerRequest &req); void WebSocketCPUResume(DebuggerRequest &req); +void WebSocketCPUStatus(DebuggerRequest &req); void WebSocketCPUGetAllRegs(DebuggerRequest &req); void WebSocketCPUGetReg(DebuggerRequest &req); void WebSocketCPUSetReg(DebuggerRequest &req); From f02bd4daff968d44c9c348cec93cd3ef64d4754b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 19 Apr 2018 21:14:01 -0700 Subject: [PATCH 174/301] Debugger: Use a counter when entering stepping. This allows us to poll coreState and know if a new stepping needs to be sent. This is useful to allow e.g. regs to show changes per step. --- Core/Debugger/WebSocket/SteppingBroadcaster.cpp | 15 +++++++++------ Core/Debugger/WebSocket/SteppingBroadcaster.h | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp index 89e91bc63d..35b1a8c124 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -21,15 +21,18 @@ #include "Core/System.h" void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { - // TODO: This is somewhat primitive. It'd be nice to register a callback with Core instead? - if (coreState != prevState_ && PSP_IsInited()) { - // We ignore CORE_POWERDOWN. - if (coreState == CORE_STEPPING) { + if (PSP_IsInited()) { + int steppingCounter = Core_GetSteppingCounter(); + // We ignore CORE_POWERDOWN as a stepping state. + if (coreState == CORE_STEPPING && steppingCounter != lastCounter_) { // TODO: Should send more data proactively. ws->Send(R"({"event":"cpu.stepping"})"); - } else if (prevState_ == CORE_STEPPING && Core_IsActive()) { + } else if (prevState_ == CORE_STEPPING && coreState != CORE_STEPPING && Core_IsActive()) { ws->Send(R"({"event":"cpu.resume"})"); } + lastCounter_ = steppingCounter; + prevState_ = coreState; + } else { + prevState_ = CORE_POWERDOWN; } - prevState_ = coreState; } diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.h b/Core/Debugger/WebSocket/SteppingBroadcaster.h index df196d9ff8..4ca34f4552 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.h +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.h @@ -33,4 +33,5 @@ public: private: CoreState prevState_; + int lastCounter_ = 0; }; From f81fa27abe138b418af5a362a16650151e76e489 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 21 Apr 2018 13:54:44 -0700 Subject: [PATCH 175/301] Debugger: Add config for remote debugger on start. --- Core/Config.cpp | 1 + Core/Config.h | 1 + UI/NativeApp.cpp | 8 +++++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 51b232e0eb..b458914d10 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -380,6 +380,7 @@ static ConfigSetting generalSettings[] = { ConfigSetting("RemoteISOManualConfig", &g_Config.bRemoteISOManual, false), ConfigSetting("RemoteShareOnStartup", &g_Config.bRemoteShareOnStartup, false), ConfigSetting("RemoteISOSubdir", &g_Config.sRemoteISOSubdir, "/"), + ConfigSetting("RemoteDebuggerOnStartup", &g_Config.bRemoteDebuggerOnStartup, false), #ifdef __ANDROID__ ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, 1), diff --git a/Core/Config.h b/Core/Config.h index 05a4824203..e423013842 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -141,6 +141,7 @@ public: bool bRemoteISOManual; bool bRemoteShareOnStartup; std::string sRemoteISOSubdir; + bool bRemoteDebuggerOnStartup; bool bMemStickInserted; int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms. diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 8ae0bedb41..5e8b8deaf0 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -587,10 +587,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch screenManager->switchScreen(new LogoScreen()); } - if (g_Config.bRemoteShareOnStartup) { - // TODO: Separate config options. + if (g_Config.bRemoteShareOnStartup && g_Config.bRemoteDebuggerOnStartup) StartWebServer(WebServerFlags::ALL); - } + else if (g_Config.bRemoteShareOnStartup) + StartWebServer(WebServerFlags::DISCS); + else if (g_Config.bRemoteDebuggerOnStartup) + StartWebServer(WebServerFlags::DEBUGGER); std::string sysName = System_GetProperty(SYSPROP_NAME); isOuya = KeyMap::IsOuya(sysName); From a2d82cb6547ad30b778ae940e10d0d5a8d5c812f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 21 Apr 2018 14:17:33 -0700 Subject: [PATCH 176/301] Debugger: Add checkbox to enable remote debugger. It's not a setting though, so maybe a button would be better. Or, it could be the startup setting... --- UI/GameSettingsScreen.cpp | 24 ++++++++++++++++++++++++ UI/GameSettingsScreen.h | 6 +++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index cced4b40f3..0ad1a44cf5 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -52,6 +52,7 @@ #include "Core/Host.h" #include "Core/System.h" #include "Core/Reporting.h" +#include "Core/WebServer.h" #include "GPU/Common/PostShader.h" #include "android/jni/TestRunner.h" #include "GPU/GPUInterface.h" @@ -1270,6 +1271,12 @@ void DeveloperToolsScreen::CreateViews() { createTextureIni->SetEnabled(false); } #endif + + allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); + canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); + CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger")); + list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger); + allowDebugger->SetEnabledPtr(&canAllowDebugger_); } void DeveloperToolsScreen::onFinish(DialogResult result) { @@ -1371,6 +1378,23 @@ UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) { + if (allowDebugger_) { + StartWebServer(WebServerFlags::DEBUGGER); + } else { + StopWebServer(WebServerFlags::DEBUGGER); + } + // Persist the setting. Maybe should separate? + g_Config.bRemoteDebuggerOnStartup = allowDebugger_; + return UI::EVENT_CONTINUE; +} + +void DeveloperToolsScreen::update() { + UIDialogScreenWithBackground::update(); + allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); + canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); +} + void ProAdhocServerScreen::CreateViews() { using namespace UI; I18NCategory *sy = GetI18NCategory("System"); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 3acad0dcfe..c68fb8939a 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -140,13 +140,13 @@ private: class DeveloperToolsScreen : public UIDialogScreenWithBackground { public: DeveloperToolsScreen() {} + void update() override; void onFinish(DialogResult result) override; protected: void CreateViews() override; private: - UI::EventReturn OnBack(UI::EventParams &e); UI::EventReturn OnRunCPUTests(UI::EventParams &e); UI::EventReturn OnLoggingChanged(UI::EventParams &e); UI::EventReturn OnLoadLanguageIni(UI::EventParams &e); @@ -154,6 +154,10 @@ private: UI::EventReturn OnOpenTexturesIniFile(UI::EventParams &e); UI::EventReturn OnLogConfig(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); + UI::EventReturn OnRemoteDebugger(UI::EventParams &e); + + bool allowDebugger_ = false; + bool canAllowDebugger_ = true; }; class ProAdhocServerScreen : public UIDialogScreenWithBackground { From 6bec3db3fb623c193583cb1c1002399085f42dfa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 21 Apr 2018 15:33:34 -0700 Subject: [PATCH 177/301] Debugger: Disconnect on shutdown/disable. Although, it could be made safe to keep them up when restarting with debugging still enabled. --- Core/Debugger/WebSocket.cpp | 31 +++++++++++++++++++++++++++++++ Core/Debugger/WebSocket.h | 2 ++ Core/WebServer.cpp | 2 ++ ext/native/net/http_server.cpp | 2 +- ext/native/net/http_server.h | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 6343a8331d..d42fdbd3a2 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -15,6 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include #include "thread/threadutil.h" #include "Core/Debugger/WebSocket.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" @@ -54,12 +56,25 @@ static const std::vector subscribers({ { &WebSocketCPUCoreInit, nullptr }, }); +// To handle webserver restart, keep track of how many running. +static volatile int debuggersConnected = 0; +static volatile bool stopRequested = false; +static std::mutex stopLock; +static std::condition_variable stopCond; + +static void UpdateConnected(int delta) { + std::lock_guard guard(stopLock); + debuggersConnected += delta; + stopCond.notify_all(); +} + void HandleDebuggerRequest(const http::Request &request) { net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org"); if (!ws) return; setCurrentThreadName("Debugger"); + UpdateConnected(1); LogBroadcaster logger; GameBroadcaster game; @@ -103,6 +118,10 @@ void HandleDebuggerRequest(const http::Request &request) { logger.Broadcast(ws); game.Broadcast(ws); stepping.Broadcast(ws); + + if (stopRequested) { + ws->Close(net::WebSocketClose::GOING_AWAY); + } } for (size_t i = 0; i < subscribers.size(); ++i) { @@ -114,4 +133,16 @@ void HandleDebuggerRequest(const http::Request &request) { } delete ws; + UpdateConnected(-1); +} + +void StopAllDebuggers() { + std::unique_lock guard(stopLock); + while (debuggersConnected != 0) { + stopRequested = true; + stopCond.wait(guard); + } + + // Reset it back for next time. + stopRequested = false; } diff --git a/Core/Debugger/WebSocket.h b/Core/Debugger/WebSocket.h index 73b4e5fe8f..0151ca686a 100644 --- a/Core/Debugger/WebSocket.h +++ b/Core/Debugger/WebSocket.h @@ -22,3 +22,5 @@ class Request; } void HandleDebuggerRequest(const http::Request &request); +// Note: blocks. +void StopAllDebuggers(); diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index d0f7c39fe7..f5fdf68754 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -216,6 +216,8 @@ static void ExecuteWebServer() { } http->Stop(); + StopAllDebuggers(); + delete http; // Move to STARTING to lock flags/STOPPING. if (UpdateStatus(ServerStatus::STARTING, ServerStatus::RESTARTING)) { diff --git a/ext/native/net/http_server.cpp b/ext/native/net/http_server.cpp index ace77907c4..c77dd37169 100644 --- a/ext/native/net/http_server.cpp +++ b/ext/native/net/http_server.cpp @@ -279,7 +279,7 @@ void Server::HandleConnection(int conn_fd) { WLOG("Bad request, ignoring."); return; } - HandleRequestDefault(request); + HandleRequest(request); // TODO: Way to mark the content body as read, read it here if never read. // This allows the handler to stream if need be. diff --git a/ext/native/net/http_server.h b/ext/native/net/http_server.h index 5220bda762..6fc97d907b 100644 --- a/ext/native/net/http_server.h +++ b/ext/native/net/http_server.h @@ -63,6 +63,7 @@ private: class Server { public: Server(threading::Executor *executor); + virtual ~Server() {} typedef std::function UrlHandlerFunc; typedef std::map UrlHandlerMap; From 77131e737bbf923b14e51a565331d471afcd5fdf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 21 Apr 2018 17:31:29 -0700 Subject: [PATCH 178/301] Log: Use a separate field for the timestamp. Better than parsing it since we already have the header separate. Simpler too. --- Common/ConsoleListener.cpp | 2 +- Common/LogManager.cpp | 18 ++++++---------- Common/LogManager.h | 3 ++- Core/Debugger/WebSocket/LogBroadcaster.cpp | 24 ++++++++++++++-------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index 34491ec49f..b5285e1f25 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -600,7 +600,7 @@ void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool void ConsoleListener::Log(const LogMessage &msg) { char Text[2048]; - snprintf(Text, sizeof(Text), "%s %s", msg.header, msg.msg.c_str()); + snprintf(Text, sizeof(Text), "%s %s %s", msg.timestamp, msg.header, msg.msg.c_str()); Text[sizeof(Text) - 2] = '\n'; Text[sizeof(Text) - 1] = '\0'; diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index c138f34533..65545b1fc0 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -225,22 +225,17 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const if (fileshort != file) file = fileshort + 1; } - - char formattedTime[13]; std::lock_guard lk(log_lock_); - Common::Timer::GetTimeFormatted(formattedTime); + Common::Timer::GetTimeFormatted(message.timestamp); - size_t prefixLen; if (hleCurrentThreadName) { - prefixLen = snprintf(message.header, sizeof(message.header), "%s %-12.12s %c[%s]: %s:%d", - formattedTime, + snprintf(message.header, sizeof(message.header), "%-12.12s %c[%s]: %s:%d", hleCurrentThreadName, level_to_char[(int)level], log.m_shortName, file, line); } else { - prefixLen = snprintf(message.header, sizeof(message.header), "%s %s:%d %c[%s]:", - formattedTime, + snprintf(message.header, sizeof(message.header), "%s:%d %c[%s]:", file, line, level_to_char[(int)level], log.m_shortName); } @@ -250,15 +245,14 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const va_copy(args_copy, args); size_t neededBytes = vsnprintf(msgBuf, sizeof(msgBuf), format, args); + message.msg.resize(neededBytes + 1); if (neededBytes > sizeof(msgBuf)) { // Needed more space? Re-run vsnprintf. - message.msg.resize(neededBytes + 1); vsnprintf(&message.msg[0], neededBytes + 1, format, args_copy); } else { - message.msg.resize(neededBytes + 1); memcpy(&message.msg[0], msgBuf, neededBytes); } - message.msg[message.msg.size() - 1] = '\n'; + message.msg[neededBytes] = '\n'; va_end(args_copy); std::lock_guard listeners_lock(listeners_lock_); @@ -313,7 +307,7 @@ void FileLogListener::Log(const LogMessage &message) { return; std::lock_guard lk(m_log_lock); - m_logfile << message.header << " " << message.msg << std::flush; + m_logfile << message.timestamp << " " << message.header << " " << message.msg << std::flush; } void OutputDebugStringLogListener::Log(const LogMessage &message) { diff --git a/Common/LogManager.h b/Common/LogManager.h index 3a778ba11c..1e8e39547d 100644 --- a/Common/LogManager.h +++ b/Common/LogManager.h @@ -34,7 +34,8 @@ extern const char *hleCurrentThreadName; // Struct that listeners can output how they want. For example, on Android we don't want to add // timestamp or write the level as a string, those already exist. struct LogMessage { - char header[64]; // timestamp and the other stuff in front... + char timestamp[16]; + char header[64]; // Filename/thread/etc. in front. LogTypes::LOG_LEVELS level; const char *log; std::string msg; // The actual log message. diff --git a/Core/Debugger/WebSocket/LogBroadcaster.cpp b/Core/Debugger/WebSocket/LogBroadcaster.cpp index 2474294e0a..07070be7b0 100644 --- a/Core/Debugger/WebSocket/LogBroadcaster.cpp +++ b/Core/Debugger/WebSocket/LogBroadcaster.cpp @@ -86,27 +86,33 @@ LogBroadcaster::~LogBroadcaster() { } struct DebuggerLogEvent { - std::string header; - std::string message; - int level; - const char *channel; + const LogMessage &l; operator std::string() { JsonWriter j; j.begin(); j.writeString("event", "log"); - j.writeString("header", header); - j.writeString("message", message); - j.writeInt("level", level); - j.writeString("channel", channel); + j.writeString("timestamp", l.timestamp); + j.writeString("header", l.header); + j.writeString("message", l.msg); + j.writeInt("level", l.level); + j.writeString("channel", l.log); j.end(); return j.str(); } }; +// Log message (log) +// +// Sent unexpectedly with these properties: +// - timestamp: string timestamp of event. +// - header: string header information about the event (including file/line.) +// - message: actual log message as a string. +// - level: number severity level (1 = highest.) +// - channel: string describing log channel / grouping. void LogBroadcaster::Broadcast(net::WebSocketServer *ws) { auto messages = listener_->GetMessages(); for (auto msg : messages) { - ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log}); + ws->Send(DebuggerLogEvent{msg}); } } From 3dac5f21032694a338536139b4c1eadd50ae8a67 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 21 Apr 2018 18:26:36 -0700 Subject: [PATCH 179/301] Debugger: Add current game info. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 ++ Core/Debugger/WebSocket.cpp | 2 + Core/Debugger/WebSocket/GameBroadcaster.cpp | 74 ++++++++++++++++++--- Core/Debugger/WebSocket/GameSubscriber.cpp | 51 ++++++++++++++ Core/Debugger/WebSocket/GameSubscriber.h | 24 +++++++ Core/System.cpp | 4 ++ Core/System.h | 1 + android/jni/Android.mk | 1 + 10 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 Core/Debugger/WebSocket/GameSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/GameSubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ea61d912b..9d8d1eb295 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1408,6 +1408,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/CPUCoreSubscriber.h Core/Debugger/WebSocket/GameBroadcaster.cpp Core/Debugger/WebSocket/GameBroadcaster.h + Core/Debugger/WebSocket/GameSubscriber.cpp + Core/Debugger/WebSocket/GameSubscriber.h Core/Debugger/WebSocket/LogBroadcaster.cpp Core/Debugger/WebSocket/LogBroadcaster.h Core/Debugger/WebSocket/SteppingBroadcaster.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c102fd2cb7..4e8826e6e1 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -187,6 +187,7 @@ + @@ -539,6 +540,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 3a8a72a82e..e0d5840131 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -713,6 +713,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1313,6 +1316,9 @@ Debugger\WebSocket + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index d42fdbd3a2..2f5aafa403 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -44,6 +44,7 @@ #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" +#include "Core/Debugger/WebSocket/GameSubscriber.h" typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); typedef void (*Subscribershutdown)(void *p); @@ -54,6 +55,7 @@ struct SubscriberInfo { static const std::vector subscribers({ { &WebSocketCPUCoreInit, nullptr }, + { &WebSocketGameInit, nullptr }, }); // To handle webserver restart, keep track of how many running. diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp index 37a9e2c919..4c53522a98 100644 --- a/Core/Debugger/WebSocket/GameBroadcaster.cpp +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -17,22 +17,78 @@ #include "Core/Debugger/WebSocket/GameBroadcaster.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/ELF/ParamSFO.h" #include "Core/System.h" +struct GameStatusEvent { + const char *ev; + + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", ev); + if (PSP_IsInited()) { + j.pushDict("game"); + j.writeString("id", g_paramSFO.GetDiscID()); + j.writeString("version", g_paramSFO.GetValueString("DISC_VERSION")); + j.writeString("title", g_paramSFO.GetValueString("TITLE")); + j.pop(); + } else { + j.writeRaw("game", "null"); + } + j.end(); + return j.str(); + } +}; + +// Game started (game.start) +// +// Sent unexpectedly with these properties: +// - game: null or an object with properties: +// - id: string disc ID (such as ULUS12345.) +// - version: string disc version. +// - title: string game title. + +// Game quit / ended (game.quit) +// +// Sent unexpectedly with these properties: +// - game: null + +// Game paused (game.pause) +// +// Note: this is not the same as stepping. This means the user went to the pause menu. +// +// Sent unexpectedly with these properties: +// - game: null or an object with properties: +// - id: string disc ID (such as ULUS12345.) +// - version: string disc version. +// - title: string game title. + +// Game resumed (game.pause) +// +// Note: this is not the same as stepping. This means the user resumed from the pause menu. +// +// Sent unexpectedly with these properties: +// - game: null or an object with properties: +// - id: string disc ID (such as ULUS12345.) +// - version: string disc version. +// - title: string game title. void GameBroadcaster::Broadcast(net::WebSocketServer *ws) { - // TODO: This is ugly. Implement proper information instead. - // TODO: Should probably include info about which game, etc. + // TODO: This is ugly. Callbacks instead? GlobalUIState state = GetUIState(); if (prevState_ != state) { if (state == UISTATE_PAUSEMENU) { - ws->Send(R"({"event":"game.pause"})"); + ws->Send(GameStatusEvent{"game.pause"}); + prevState_ = state; } else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) { - ws->Send(R"({"event":"game.resume"})"); - } else if (state == UISTATE_INGAME) { - ws->Send(R"({"event":"game.start"})"); - } else if (state == UISTATE_MENU) { - ws->Send(R"({"event":"game.quit"})"); + ws->Send(GameStatusEvent{"game.resume"}); + prevState_ = state; + } else if (state == UISTATE_INGAME && PSP_IsInited()) { + ws->Send(GameStatusEvent{"game.start"}); + prevState_ = state; + } else if (state == UISTATE_MENU && !PSP_IsInited() && !PSP_IsQuitting()) { + ws->Send(GameStatusEvent{"game.quit"}); + prevState_ = state; } - prevState_ = state; } } diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp new file mode 100644 index 0000000000..2e91505ac2 --- /dev/null +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Debugger/WebSocket/GameSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/ELF/ParamSFO.h" +#include "Core/System.h" + +void *WebSocketGameInit(DebuggerEventHandlerMap &map) { + map["game.status"] = &WebSocketGameStatus; + + return nullptr; +} + +// Check game status (game.status) +// +// No parameters. +// +// Response (same event name): +// - game: null or an object with properties: +// - id: string disc ID (such as ULUS12345.) +// - version: string disc version. +// - title: string game title. +// - paused: boolean, true when gameplay is paused (not the same as stepping.) +void WebSocketGameStatus(DebuggerRequest &req) { + JsonWriter &json = req.Respond(); + if (PSP_IsInited()) { + json.pushDict("game"); + json.writeString("id", g_paramSFO.GetDiscID()); + json.writeString("version", g_paramSFO.GetValueString("DISC_VERSION")); + json.writeString("title", g_paramSFO.GetValueString("TITLE")); + json.pop(); + } else { + json.writeRaw("game", "null"); + } + json.writeBool("paused", GetUIState() == UISTATE_PAUSEMENU); +} diff --git a/Core/Debugger/WebSocket/GameSubscriber.h b/Core/Debugger/WebSocket/GameSubscriber.h new file mode 100644 index 0000000000..4236b54885 --- /dev/null +++ b/Core/Debugger/WebSocket/GameSubscriber.h @@ -0,0 +1,24 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketGameInit(DebuggerEventHandlerMap &map); + +void WebSocketGameStatus(DebuggerRequest &req); diff --git a/Core/System.cpp b/Core/System.cpp index 6355c66dd3..b59a365872 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -385,6 +385,10 @@ bool PSP_IsInited() { return pspIsInited && !pspIsQuitting; } +bool PSP_IsQuitting() { + return pspIsQuitting; +} + void PSP_Shutdown() { // Do nothing if we never inited. if (!pspIsInited && !pspIsIniting && !pspIsQuitting) { diff --git a/Core/System.h b/Core/System.h index 32182c3bc2..f25974970e 100644 --- a/Core/System.h +++ b/Core/System.h @@ -66,6 +66,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string); bool PSP_InitUpdate(std::string *error_string); bool PSP_IsIniting(); bool PSP_IsInited(); +bool PSP_IsQuitting(); void PSP_Shutdown(); void PSP_BeginHostFrame(); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b5b8dda1d2..9e32c054a7 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -303,6 +303,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket.cpp \ $(SRC)/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \ From 05c560b52d3082a8d8ce6c45c9e4408d8e816c19 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Apr 2018 00:04:28 -0700 Subject: [PATCH 180/301] Debugger: Include PC and ticks in stepping events. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 7 +++++ .../WebSocket/SteppingBroadcaster.cpp | 27 +++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 2453675a33..3d42a46df3 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -17,6 +17,7 @@ #include "Common/StringUtils.h" #include "Core/Core.h" +#include "Core/CoreTiming.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/MIPS/MIPS.h" @@ -79,10 +80,16 @@ void WebSocketCPUResume(DebuggerRequest &req) { // Response (same event name): // - stepping: boolean, CPU currently stepping. // - paused: boolean, CPU paused or not started yet. +// - pc: number value of PC register (inaccurate unless stepping.) +// - ticks: number of CPU cycles into emulation. void WebSocketCPUStatus(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeBool("stepping", PSP_IsInited() && Core_IsStepping() && coreState != CORE_POWERDOWN); json.writeBool("paused", GetUIState() != UISTATE_INGAME); + // Avoid NULL deference. + json.writeFloat("pc", PSP_IsInited() ? currentMIPS->pc : 0); + // A double ought to be good enough for a 156 day debug session. + json.writeFloat("ticks", PSP_IsInited() ? CoreTiming::GetTicks() : 0); } // Retrieve all regs and their values (cpu.getAllRegs) diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp index 35b1a8c124..9b9cbf3200 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -16,17 +16,40 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Core/Core.h" +#include "Core/CoreTiming.h" #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MIPS/MIPS.h" #include "Core/System.h" +struct CPUSteppingEvent { + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", "cpu.stepping"); + j.writeFloat("pc", currentMIPS->pc); + // A double ought to be good enough for a 156 day debug session. + j.writeFloat("ticks", CoreTiming::GetTicks()); + j.end(); + return j.str(); + } +}; + +// CPU has begun stepping (cpu.stepping) +// +// Sent unexpectedly with these properties: +// - pc: number value of PC register (inaccurate unless stepping.) +// - ticks: number of CPU cycles into emulation. + +// CPU has resumed from stepping (cpu.resume) +// +// Sent unexpectedly with no other properties. void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { if (PSP_IsInited()) { int steppingCounter = Core_GetSteppingCounter(); // We ignore CORE_POWERDOWN as a stepping state. if (coreState == CORE_STEPPING && steppingCounter != lastCounter_) { - // TODO: Should send more data proactively. - ws->Send(R"({"event":"cpu.stepping"})"); + ws->Send(CPUSteppingEvent()); } else if (prevState_ == CORE_STEPPING && coreState != CORE_STEPPING && Core_IsActive()) { ws->Send(R"({"event":"cpu.resume"})"); } From b756d921399e728d9f4669c8becdf3ca7d536525 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Apr 2018 00:28:41 -0700 Subject: [PATCH 181/301] Debugger: Add version event for future proofing. Game isn't precisely right, but it's close enough. --- Core/Debugger/WebSocket.cpp | 4 ++++ Core/Debugger/WebSocket/GameSubscriber.cpp | 16 ++++++++++++++++ Core/Debugger/WebSocket/GameSubscriber.h | 1 + 3 files changed, 21 insertions(+) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 2f5aafa403..e99253b813 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -38,6 +38,10 @@ // - "message": A string describing what happened. // - "level": Integer severity level. (1 = NOTICE, 2 = ERROR, 3 = WARN, 4 = INFO, 5 = DEBUG, 6 = VERBOSE) // - "ticket": Optional, present if in response to an event with a "ticket" field, simply repeats that value. +// +// At start, please send a "version" event. See WebSocket/GameSubscriber.cpp for more details. +// +// For other events, look inside Core/Debugger/WebSocket/ for details on each event. #include "Core/Debugger/WebSocket/GameBroadcaster.h" #include "Core/Debugger/WebSocket/LogBroadcaster.h" diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp index 2e91505ac2..e2241ac04e 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.cpp +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -22,6 +22,7 @@ void *WebSocketGameInit(DebuggerEventHandlerMap &map) { map["game.status"] = &WebSocketGameStatus; + map["version"] = &WebSocketVersion; return nullptr; } @@ -49,3 +50,18 @@ void WebSocketGameStatus(DebuggerRequest &req) { } json.writeBool("paused", GetUIState() == UISTATE_PAUSEMENU); } + +// Notify debugger version info (version) +// +// Parameters: +// - name: string indicating name of app or tool. +// - version: string version. +// +// Response (same event name): +// - name: string, "PPSSPP" unless some special build. +// - version: string, typically starts with "v" and may have git build info. +void WebSocketVersion(DebuggerRequest &req) { + JsonWriter &json = req.Respond(); + json.writeString("name", "PPSSPP"); + json.writeString("version", PPSSPP_GIT_VERSION); +} diff --git a/Core/Debugger/WebSocket/GameSubscriber.h b/Core/Debugger/WebSocket/GameSubscriber.h index 4236b54885..77cec9e38b 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.h +++ b/Core/Debugger/WebSocket/GameSubscriber.h @@ -22,3 +22,4 @@ void *WebSocketGameInit(DebuggerEventHandlerMap &map); void WebSocketGameStatus(DebuggerRequest &req); +void WebSocketVersion(DebuggerRequest &req); From 4da97b3f9e999bd12d531b4f3dcba50c0d099e3b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Apr 2018 08:33:22 -0700 Subject: [PATCH 182/301] Debugger: Lock during startup/shutdown. Otherwise things can get freed while we're trying to inspect them. --- Core/Debugger/WebSocket.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index e99253b813..5b323bce4b 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -68,12 +68,43 @@ static volatile bool stopRequested = false; static std::mutex stopLock; static std::condition_variable stopCond; +// Prevent threading surprises and obscure crashes by locking startup/shutdown. +static bool lifecycleLockSetup = false; +static std::mutex lifecycleLock; + static void UpdateConnected(int delta) { std::lock_guard guard(stopLock); debuggersConnected += delta; stopCond.notify_all(); } +static void WebSocketNotifyLifecycle(CoreLifecycle stage) { + switch (stage) { + case CoreLifecycle::STARTING: + case CoreLifecycle::STOPPING: + if (debuggersConnected > 0) { + DEBUG_LOG(SYSTEM, "Waiting for debugger to complete on shutdown"); + } + lifecycleLock.lock(); + break; + + case CoreLifecycle::START_COMPLETE: + case CoreLifecycle::STOPPED: + lifecycleLock.unlock(); + if (debuggersConnected > 0) { + DEBUG_LOG(SYSTEM, "Debugger ready for shutdown"); + } + break; + } +} + +static void SetupDebuggerLock() { + if (!lifecycleLockSetup) { + Core_ListenLifecycle(&WebSocketNotifyLifecycle); + lifecycleLockSetup = true; + } +} + void HandleDebuggerRequest(const http::Request &request) { net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org"); if (!ws) @@ -81,6 +112,7 @@ void HandleDebuggerRequest(const http::Request &request) { setCurrentThreadName("Debugger"); UpdateConnected(1); + SetupDebuggerLock(); LogBroadcaster logger; GameBroadcaster game; @@ -89,6 +121,7 @@ void HandleDebuggerRequest(const http::Request &request) { std::unordered_map eventHandlers; std::vector subscriberData; for (auto info : subscribers) { + std::lock_guard guard(lifecycleLock); subscriberData.push_back(info.init(eventHandlers)); } @@ -109,6 +142,7 @@ void HandleDebuggerRequest(const http::Request &request) { DebuggerRequest req(event, ws, root); auto eventFunc = eventHandlers.find(event); if (eventFunc != eventHandlers.end()) { + std::lock_guard guard(lifecycleLock); eventFunc->second(req); req.Finish(); } else { @@ -120,6 +154,7 @@ void HandleDebuggerRequest(const http::Request &request) { }); while (ws->Process(1.0f / 60.0f)) { + std::lock_guard guard(lifecycleLock); // These send events that aren't just responses to requests. logger.Broadcast(ws); game.Broadcast(ws); @@ -130,6 +165,7 @@ void HandleDebuggerRequest(const http::Request &request) { } } + std::lock_guard guard(lifecycleLock); for (size_t i = 0; i < subscribers.size(); ++i) { if (subscribers[i].shutdown) { subscribers[i].shutdown(subscriberData[i]); From 944948a5f4ce172a78fe1995c99d89a9e79cabe4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Apr 2018 20:35:24 -0700 Subject: [PATCH 183/301] Debugger: Initial disasm API. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Debugger/WebSocket.cpp | 2 + Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 2 +- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 244 ++++++++++++++++++ Core/Debugger/WebSocket/DisasmSubscriber.h | 23 ++ Core/Debugger/WebSocket/WebSocketUtils.cpp | 108 +++++--- Core/Debugger/WebSocket/WebSocketUtils.h | 11 +- android/jni/Android.mk | 2 + 10 files changed, 366 insertions(+), 36 deletions(-) create mode 100644 Core/Debugger/WebSocket/DisasmSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/DisasmSubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d8d1eb295..b61d39d89b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1406,6 +1406,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket.h Core/Debugger/WebSocket/CPUCoreSubscriber.cpp Core/Debugger/WebSocket/CPUCoreSubscriber.h + Core/Debugger/WebSocket/DisasmSubscriber.cpp + Core/Debugger/WebSocket/DisasmSubscriber.h Core/Debugger/WebSocket/GameBroadcaster.cpp Core/Debugger/WebSocket/GameBroadcaster.h Core/Debugger/WebSocket/GameSubscriber.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 4e8826e6e1..d21c391324 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -189,6 +189,7 @@ + @@ -541,6 +542,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index e0d5840131..d9d65387df 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -716,6 +716,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1319,6 +1322,9 @@ Debugger\WebSocket + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 5b323bce4b..390dd4fa00 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -48,6 +48,7 @@ #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" +#include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h" typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); @@ -59,6 +60,7 @@ struct SubscriberInfo { static const std::vector subscribers({ { &WebSocketCPUCoreInit, nullptr }, + { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, { &WebSocketGameInit, nullptr }, }); diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 3d42a46df3..2d6ee2f9e8 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -296,7 +296,7 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { } uint32_t val; - if (!req.ParamU32OrFloatBits("value", &val)) { + if (!req.ParamU32("value", &val, true)) { // Already sent error. return; } diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp new file mode 100644 index 0000000000..37b4bc655c --- /dev/null +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -0,0 +1,244 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "base/stringutil.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/WebSocket/DisasmSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MemMap.h" +#include "Core/MIPS/MIPSDebugInterface.h" + +struct WebSocketDisasmState { + WebSocketDisasmState() { + disasm_.setCpu(currentDebugMIPS); + } + + void Base(DebuggerRequest &req); + void Disasm(DebuggerRequest &req); + +protected: + void WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l); + void WriteBranchGuide(JsonWriter &json, const BranchLine &l); + + DisassemblyManager disasm_; +}; + +void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) { + auto p = new WebSocketDisasmState(); + map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1); + map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1); + + return p; +} + +void WebSocketDisasmShutdown(void *p) { + delete static_cast(p); +} + +void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l) { + u32 addr = l.info.opcodeAddress; + json.pushDict(); + if (l.type == DISTYPE_OPCODE) + json.writeString("type", "opcode"); + else if (l.type == DISTYPE_MACRO) + json.writeString("type", "macro"); + else if (l.type == DISTYPE_DATA) + json.writeString("type", "data"); + else if (l.type == DISTYPE_OTHER) + json.writeString("type", "other"); + + json.writeFloat("address", addr); + json.writeInt("addressSize", l.totalSize); + json.writeFloat("encoding", l.info.encodedOpcode.encoding); + int c = currentDebugMIPS->getColor(addr) & 0x00FFFFFF; + json.writeString("backgroundColor", StringFromFormat("#%02x%02x%02x", c & 0xFF, (c >> 8) & 0xFF, c >> 16)); + json.writeString("name", l.name); + json.writeString("params", l.params); + + const std::string addressSymbol = g_symbolMap->GetLabelString(addr); + if (addressSymbol.empty()) + json.writeRaw("symbol", "null"); + else + json.writeString("symbol", addressSymbol); + + bool enabled; + // TODO: Account for bp inside macro? + if (CBreakPoints::IsAddressBreakPoint(addr, &enabled)) { + json.pushDict("breakpoint"); + json.writeBool("enabled", enabled); + auto cond = CBreakPoints::GetBreakPointCondition(addr); + if (cond) + json.writeString("expression", cond->expressionString); + else + json.writeRaw("expression", "null"); + json.pop(); + } else { + json.writeRaw("breakpoint", "null"); + } + + json.writeBool("isCurrentPC", currentDebugMIPS->GetPC() == addr); + if (l.info.isBranch) { + json.pushDict("branch"); + if (!l.info.isBranchToRegister) { + json.writeFloat("targetAddress", l.info.branchTarget); + json.writeRaw("register", "null"); + } else { + json.writeRaw("targetAddress", "null"); + json.writeInt("register", l.info.branchRegisterNum); + } + json.writeBool("isLinked", l.info.isLinkedBranch); + json.writeBool("isLikely", l.info.isLikelyBranch); + json.pop(); + } else { + json.writeRaw("branch", "null"); + } + + if (l.info.hasRelevantAddress) { + json.pushDict("relevantData"); + json.writeFloat("address", l.info.relevantAddress); + if (Memory::IsValidRange(l.info.relevantAddress, 4)) + json.writeFloat("uintValue", Memory::ReadUnchecked_U32(l.info.relevantAddress)); + else + json.writeRaw("uintValue", "null"); + json.pop(); + } else { + json.writeRaw("relevantData", "null"); + } + + if (l.info.isConditional) + json.writeBool("conditionMet", l.info.conditionMet); + else + json.writeRaw("conditionMet", "null"); + + if (l.info.isDataAccess) { + json.pushDict("dataAccess"); + json.writeFloat("address", l.info.dataAddress); + json.writeInt("size", l.info.dataSize); + + std::string dataSymbol = g_symbolMap->GetLabelString(l.info.dataAddress); + std::string valueSymbol; + if (!Memory::IsValidRange(l.info.dataAddress, l.info.dataSize)) + json.writeRaw("uintValue", "null"); + else if (l.info.dataSize == 1) + json.writeFloat("uintValue", Memory::ReadUnchecked_U8(l.info.dataAddress)); + else if (l.info.dataSize == 2) + json.writeFloat("uintValue", Memory::ReadUnchecked_U16(l.info.dataAddress)); + else if (l.info.dataSize >= 4) { + u32 data = Memory::ReadUnchecked_U32(l.info.dataAddress); + valueSymbol = g_symbolMap->GetLabelString(data); + json.writeFloat("uintValue", data); + } + + if (!dataSymbol.empty()) + json.writeString("symbol", dataSymbol); + else + json.writeRaw("symbol", "null"); + if (!valueSymbol.empty()) + json.writeString("valueSymbol", valueSymbol); + else + json.writeRaw("valueSymbol", "null"); + json.pop(); + } else { + json.writeRaw("dataAccess", "null"); + } + + json.pop(); +} + +void WebSocketDisasmState::WriteBranchGuide(JsonWriter &json, const BranchLine &l) { + json.pushDict(); + json.writeFloat("top", l.first); + json.writeFloat("bottom", l.second); + if (l.type == LINE_UP) + json.writeString("direction", "up"); + else if (l.type == LINE_DOWN) + json.writeString("direction", "down"); + else if (l.type == LINE_RIGHT) + json.writeString("direction", "right"); + json.writeInt("lane", l.laneIndex); + json.pop(); +} + +void WebSocketDisasmState::Base(DebuggerRequest &req) { + JsonWriter &json = req.Respond(); + json.writeString("addressHex", StringFromFormat("%016llx", Memory::base)); +} + +void WebSocketDisasmState::Disasm(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { + return req.Fail("CPU not started"); + } + + uint32_t start, end; + if (!req.ParamU32("address", &start)) + return; + uint32_t count = 0; + if (!req.ParamU32("count", &count, false, DebuggerParamType::OPTIONAL)) + return; + if (count != 0) { + // Let's assume everything is two instructions. + disasm_.analyze(start - 4, count * 8 + 8); + start = disasm_.getStartAddress(start); + if (start == -1) + req.ParamU32("address", &start); + end = disasm_.getNthNextAddress(start, count); + } else if (req.ParamU32("end", &end)) { + // Let's assume everything is two instructions. + disasm_.analyze(start - 4, end - start + 8); + start = disasm_.getStartAddress(start); + if (start == -1) + req.ParamU32("address", &start); + + // Correct end and calculate count based on it. + // This accounts for macros as one line, although two instructions. + u32 stop = end; + count = 0; + for (end = start; end < stop; end = disasm_.getNthNextAddress(end, 1)) { + count++; + } + } else { + // Error message already sent. + return; + } + + bool displaySymbols = true; + if (!req.ParamBool("displaySymbols", &displaySymbols, DebuggerParamType::OPTIONAL)) + return; + + JsonWriter &json = req.Respond(); + json.pushDict("range"); + json.writeFloat("start", start); + json.writeFloat("end", end); + json.pop(); + + json.pushArray("lines"); + DisassemblyLineInfo line; + u32 addr = start; + for (u32 i = 0; i < count; ++i) { + disasm_.getLine(addr, displaySymbols, line); + WriteDisasmLine(json, line); + addr += line.totalSize; + } + json.pop(); + + json.pushArray("branchGuides"); + auto branchGuides = disasm_.getBranchLines(start, end - start); + for (auto bl : branchGuides) + WriteBranchGuide(json, bl); + json.pop(); +} diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.h b/Core/Debugger/WebSocket/DisasmSubscriber.h new file mode 100644 index 0000000000..e6c5b0e71c --- /dev/null +++ b/Core/Debugger/WebSocket/DisasmSubscriber.h @@ -0,0 +1,23 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketDisasmInit(DebuggerEventHandlerMap &map); +void WebSocketDisasmShutdown(void *p); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index 8d75122018..140ff8da82 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -74,19 +74,35 @@ static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) { return false; } -bool DebuggerRequest::ParamU32(const char *name, uint32_t *out) { +bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatBits, DebuggerParamType type) { + bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE; + bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE; + const JsonNode *node = data.get(name); if (!node) { - Fail(StringFromFormat("Missing '%s' parameter", name)); - return false; + if (required) + Fail(StringFromFormat("Missing '%s' parameter", name)); + return !required; } if (node->value.getTag() == JSON_NUMBER) { double val = node->value.toNumber(); bool isInteger = trunc(val) == val; - if (!isInteger) { - Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name)); + if (!isInteger && !allowLoose) { + // JSON doesn't give a great way to differentiate ints and floats. + // Let's play it safe and require a string. + if (allowFloatBits) + Fail(StringFromFormat("Could not parse '%s' parameter: use a string for non integer values", name)); + else + Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name)); return false; + } else if (!isInteger && allowFloatBits) { + union { + float f; + uint32_t u; + } bits = { (float)val }; + *out = bits.u; + return true; } if (val < 0 && val >= std::numeric_limits::min()) { @@ -96,61 +112,87 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out) { } else if (val >= 0 && val <= std::numeric_limits::max()) { *out = (uint32_t)val; return true; + } else if (allowLoose) { + *out = val >= 0 ? std::numeric_limits::max() : std::numeric_limits::min(); + return true; } - Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range", name)); + if (allowFloatBits) + Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range (use string for float)", name)); + else + Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range", name)); return false; } if (node->value.getTag() != JSON_STRING) { - Fail(StringFromFormat("Invalid '%s' parameter type", name)); - return false; + if (required || node->value.getTag() != JSON_NULL) { + Fail(StringFromFormat("Invalid '%s' parameter type", name)); + return false; + } + return true; } - if (U32FromString(node->value.toString(), out, false)) + if (U32FromString(node->value.toString(), out, allowFloatBits)) return true; - Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name)); + if (allowFloatBits) + Fail(StringFromFormat("Could not parse '%s' parameter: number expected", name)); + else + Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name)); return false; } -bool DebuggerRequest::ParamU32OrFloatBits(const char *name, uint32_t *out) { +bool DebuggerRequest::ParamBool(const char *name, bool *out, DebuggerParamType type) { + bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE; + bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE; + const JsonNode *node = data.get(name); if (!node) { - Fail(StringFromFormat("Missing '%s' parameter", name)); - return false; + if (required) + Fail(StringFromFormat("Missing '%s' parameter", name)); + return !required; } if (node->value.getTag() == JSON_NUMBER) { double val = node->value.toNumber(); - bool isInteger = trunc(val) == val; - - // JSON doesn't give a great way to differentiate ints and floats. - // Let's play it safe and require a string. - if (!isInteger) { - Fail(StringFromFormat("Could not parse '%s' parameter: use a string for non integer values", name)); - return false; - } - - if (val < 0 && val >= std::numeric_limits::min()) { - // Convert to unsigned representation. - *out = (uint32_t)(int32_t)val; - return true; - } else if (val >= 0 && val <= std::numeric_limits::max()) { - *out = (uint32_t)val; + if (val == 1.0 || val == 0.0 || allowLoose) { + *out = val != 0.0; return true; } - Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range (use string for float)", name)); + Fail(StringFromFormat("Could not parse '%s' parameter: should be true/1 or false/0", name)); return false; } + if (node->value.getTag() == JSON_TRUE) { + *out = true; + return true; + } + if (node->value.getTag() == JSON_FALSE) { + *out = false; + return true; + } if (node->value.getTag() != JSON_STRING) { - Fail(StringFromFormat("Invalid '%s' parameter type", name)); - return false; + if (type == DebuggerParamType::REQUIRED || node->value.getTag() != JSON_NULL) { + Fail(StringFromFormat("Invalid '%s' parameter type", name)); + return false; + } + return true; } - if (U32FromString(node->value.toString(), out, true)) + const std::string s = node->value.toString(); + if (s == "1" || s == "true") { + *out = true; return true; + } + if (s == "0" || s == "false" || (s == "" && allowLoose)) { + *out = false; + return true; + } - Fail(StringFromFormat("Could not parse '%s' parameter: number expected", name)); + if (allowLoose) { + *out = true; + return true; + } + + Fail(StringFromFormat("Could not parse '%s' parameter: boolean required", name)); return false; } diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 6e4e62d36c..132789b26d 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -59,6 +59,13 @@ struct DebuggerErrorEvent { } }; +enum class DebuggerParamType { + REQUIRED, + OPTIONAL, + REQUIRED_LOOSE, + OPTIONAL_LOOSE, +}; + struct DebuggerRequest { DebuggerRequest(const char *n, net::WebSocketServer *w, const JsonGet &d) : name(n), ws(w), data(d) { @@ -73,8 +80,8 @@ struct DebuggerRequest { responseSent_ = true; } - bool ParamU32(const char *name, uint32_t *out); - bool ParamU32OrFloatBits(const char *name, uint32_t *out); + bool ParamU32(const char *name, uint32_t *out, bool allowFloatBits = false, DebuggerParamType type = DebuggerParamType::REQUIRED); + bool ParamBool(const char *name, bool *out, DebuggerParamType type = DebuggerParamType::REQUIRED); JsonWriter &Respond(); void Finish(); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 9e32c054a7..de838dc453 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -299,9 +299,11 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/TextureReplacer.cpp \ $(SRC)/Core/WebServer.cpp \ $(SRC)/Core/Debugger/Breakpoints.cpp \ + $(SRC)/Core/Debugger/DisassemblyManager.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Debugger/WebSocket.cpp \ $(SRC)/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp \ + $(SRC)/Core/Debugger/WebSocket/DisasmSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ From 1c107308cd99d474ff3b0f0f545d92f755a47e1f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 27 Apr 2018 07:55:46 -0700 Subject: [PATCH 184/301] Debugger: Add additional encoding/symbol info. --- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 37b4bc655c..6e984e5616 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -64,7 +64,16 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi json.writeFloat("address", addr); json.writeInt("addressSize", l.totalSize); - json.writeFloat("encoding", l.info.encodedOpcode.encoding); + json.writeFloat("encoding", Memory::IsValidAddress(addr) ? Memory::Read_Instruction(addr).encoding : 0); + if (l.totalSize >= 8 && Memory::IsValidRange(addr, l.totalSize)) { + json.pushArray("macroEncoding"); + for (u32 off = 0; off < l.totalSize; off += 4) { + json.writeFloat(Memory::Read_Instruction(addr + off).encoding); + } + json.pop(); + } else { + json.writeRaw("macroEncoding", "null"); + } int c = currentDebugMIPS->getColor(addr) & 0x00FFFFFF; json.writeString("backgroundColor", StringFromFormat("#%02x%02x%02x", c & 0xFF, (c >> 8) & 0xFF, c >> 16)); json.writeString("name", l.name); @@ -94,7 +103,9 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi json.writeBool("isCurrentPC", currentDebugMIPS->GetPC() == addr); if (l.info.isBranch) { json.pushDict("branch"); + std::string targetSymbol; if (!l.info.isBranchToRegister) { + targetSymbol = g_symbolMap->GetLabelString(l.info.branchTarget); json.writeFloat("targetAddress", l.info.branchTarget); json.writeRaw("register", "null"); } else { @@ -103,6 +114,10 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi } json.writeBool("isLinked", l.info.isLinkedBranch); json.writeBool("isLikely", l.info.isLikelyBranch); + if (targetSymbol.empty()) + json.writeRaw("symbol", "null"); + else + json.writeString("symbol", targetSymbol); json.pop(); } else { json.writeRaw("branch", "null"); From 8a0e02e223919c792565471222000ee93f00a748 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Apr 2018 14:10:06 -0700 Subject: [PATCH 185/301] Debugger: Prevent crazy disasm range. Also add some documentation. --- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 44 +++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 6e984e5616..25e15bec2f 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "base/stringutil.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/DisassemblyManager.h" @@ -189,16 +190,56 @@ void WebSocketDisasmState::WriteBranchGuide(JsonWriter &json, const BranchLine & json.pop(); } +// Request the current PSP memory base address (memory.base) +// +// WARNING: Avoid this unless you have a good reason. Uses PPSSPP's address space. +// +// No parameters. +// +// Response (same event name): +// - addressHex: string indicating base address in hexadecimal (may be 64 bit.) void WebSocketDisasmState::Base(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeString("addressHex", StringFromFormat("%016llx", Memory::base)); } +// Disassemble a range of memory as CPU instructions (memory.disasm) +// +// Parameters (by count): +// - address: number specifying the start address. +// - count: number of lines to return (may be clamped to an internal limit.) +// - displaySymbols: boolean true to show symbol names in instruction params. +// +// Parameters (by end address): +// - address: number specifying the start address. +// - end: number which must be after the start address (may be clamped to an internal limit.) +// - displaySymbols: boolean true to show symbol names in instruction params. +// +// Response (same event name): +// - range: object with result "start" and "end" properties, the addresses actually used. +// (disassembly may have snapped to a nearby instruction.) +// - branchGuides: array of objects: +// - top: the earlier address as a number. +// - bottom: the later address as a number. +// - direction: "up", "down", or "right" depending on the flow of the branch. +// - lane: number index to avoid overlapping guides. +// - lines: array of objects: +// - type: "opcode", "macro", "data", or "other". +// - address: address of first actual instruction. +// - addressSize: bytes used by this line (might be more than 4.) +// - encoding: uint value of actual instruction (may differ from memory read when using jit.) +// - macroEncoding: null, or an array of encodings if this line represents multiple instructions. +// - name: string name of the instruction. +// - params: formatted parameters for the instruction. +// - (other info about the disassembled line.) void WebSocketDisasmState::Disasm(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } + // In case of client errors, we limit the range to something that won't make us crash. + static const uint32_t MAX_RANGE = 10000; + uint32_t start, end; if (!req.ParamU32("address", &start)) return; @@ -206,6 +247,7 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { if (!req.ParamU32("count", &count, false, DebuggerParamType::OPTIONAL)) return; if (count != 0) { + count = std::min(count, MAX_RANGE); // Let's assume everything is two instructions. disasm_.analyze(start - 4, count * 8 + 8); start = disasm_.getStartAddress(start); @@ -213,12 +255,12 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { req.ParamU32("address", &start); end = disasm_.getNthNextAddress(start, count); } else if (req.ParamU32("end", &end)) { + end = std::min(std::max(start, end), start + MAX_RANGE * 4); // Let's assume everything is two instructions. disasm_.analyze(start - 4, end - start + 8); start = disasm_.getStartAddress(start); if (start == -1) req.ParamU32("address", &start); - // Correct end and calculate count based on it. // This accounts for macros as one line, although two instructions. u32 stop = end; From f66738eac705d39378b1a2de60bd6c648433fd4b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 19:30:14 -0700 Subject: [PATCH 186/301] Debugger: Add cpu.evaluate method. Probably useful for a bunch of things, really. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 34 ++++++++++ Core/Debugger/WebSocket/CPUCoreSubscriber.h | 1 + Core/Debugger/WebSocket/WebSocketUtils.cpp | 63 ++++++++++++++++--- Core/Debugger/WebSocket/WebSocketUtils.h | 1 + 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 2d6ee2f9e8..1b795899a7 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -31,6 +31,7 @@ void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) { map["cpu.getAllRegs"] = &WebSocketCPUGetAllRegs; map["cpu.getReg"] = &WebSocketCPUGetReg; map["cpu.setReg"] = &WebSocketCPUSetReg; + map["cpu.evaluate"] = &WebSocketCPUEvaluate; return nullptr; } @@ -334,3 +335,36 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { json.writeFloat("uintValue", val); json.writeString("floatValue", RegValueAsFloat(val)); } + +// Evaluate an expression (cpu.evaluate) +// +// Parameters: +// - expression: string containing labels, operators, regs, etc. +// +// Response (same event name): +// - uintValue: value in register. +// - floatValue: string showing float representation. May be "nan", "inf", or "-inf". +void WebSocketCPUEvaluate(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + std::string exp; + if (!req.ParamString("expression", &exp)) { + // Already sent error. + return; + } + + u32 val; + PostfixExpression postfix; + if (!currentDebugMIPS->initExpression(exp.c_str(), postfix)) { + return req.Fail(StringFromFormat("Could not parse expression syntax: %s", getExpressionError())); + } + if (!currentDebugMIPS->parseExpression(postfix, val)) { + return req.Fail(StringFromFormat("Could not evaluate expression: %s", getExpressionError())); + } + + JsonWriter &json = req.Respond(); + json.writeFloat("uintValue", val); + json.writeString("floatValue", RegValueAsFloat(val)); +} diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.h b/Core/Debugger/WebSocket/CPUCoreSubscriber.h index effff73a76..3667979e96 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.h +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.h @@ -27,3 +27,4 @@ void WebSocketCPUStatus(DebuggerRequest &req); void WebSocketCPUGetAllRegs(DebuggerRequest &req); void WebSocketCPUGetReg(DebuggerRequest &req); void WebSocketCPUSetReg(DebuggerRequest &req); +void WebSocketCPUEvaluate(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index 140ff8da82..d21b93e9d3 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -85,7 +85,8 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatB return !required; } - if (node->value.getTag() == JSON_NUMBER) { + auto tag = node->value.getTag(); + if (tag == JSON_NUMBER) { double val = node->value.toNumber(); bool isInteger = trunc(val) == val; if (!isInteger && !allowLoose) { @@ -123,8 +124,8 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatB Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range", name)); return false; } - if (node->value.getTag() != JSON_STRING) { - if (required || node->value.getTag() != JSON_NULL) { + if (tag != JSON_STRING) { + if (required || tag != JSON_NULL) { Fail(StringFromFormat("Invalid '%s' parameter type", name)); return false; } @@ -152,7 +153,8 @@ bool DebuggerRequest::ParamBool(const char *name, bool *out, DebuggerParamType t return !required; } - if (node->value.getTag() == JSON_NUMBER) { + auto tag = node->value.getTag(); + if (tag == JSON_NUMBER) { double val = node->value.toNumber(); if (val == 1.0 || val == 0.0 || allowLoose) { *out = val != 0.0; @@ -162,16 +164,16 @@ bool DebuggerRequest::ParamBool(const char *name, bool *out, DebuggerParamType t Fail(StringFromFormat("Could not parse '%s' parameter: should be true/1 or false/0", name)); return false; } - if (node->value.getTag() == JSON_TRUE) { + if (tag == JSON_TRUE) { *out = true; return true; } - if (node->value.getTag() == JSON_FALSE) { + if (tag == JSON_FALSE) { *out = false; return true; } - if (node->value.getTag() != JSON_STRING) { - if (type == DebuggerParamType::REQUIRED || node->value.getTag() != JSON_NULL) { + if (tag != JSON_STRING) { + if (type == DebuggerParamType::REQUIRED || tag != JSON_NULL) { Fail(StringFromFormat("Invalid '%s' parameter type", name)); return false; } @@ -196,3 +198,48 @@ bool DebuggerRequest::ParamBool(const char *name, bool *out, DebuggerParamType t Fail(StringFromFormat("Could not parse '%s' parameter: boolean required", name)); return false; } + +bool DebuggerRequest::ParamString(const char *name, std::string *out, DebuggerParamType type) { + bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE; + bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE; + + const JsonNode *node = data.get(name); + if (!node) { + if (required) + Fail(StringFromFormat("Missing '%s' parameter", name)); + return !required; + } + + auto tag = node->value.getTag(); + if (tag == JSON_STRING) { + *out = node->value.toString(); + return true; + } else if (!allowLoose) { + if (required || tag != JSON_NULL) { + Fail(StringFromFormat("Invalid '%s' parameter type", name)); + return false; + } + return true; + } + + // For loose, let's allow a few things. + if (tag == JSON_TRUE) { + *out = "true"; + return true; + } else if (tag == JSON_FALSE) { + *out = "false"; + return true; + } else if (tag == JSON_NULL) { + if (required) { + *out = ""; + } + return true; + } else if (tag == JSON_NUMBER) { + // Will have a decimal place, though. + *out = StringFromFormat("%f", node->value.toNumber()); + return true; + } + + Fail(StringFromFormat("Invalid '%s' parameter type", name)); + return false; +} diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 132789b26d..1391e6614c 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -82,6 +82,7 @@ struct DebuggerRequest { bool ParamU32(const char *name, uint32_t *out, bool allowFloatBits = false, DebuggerParamType type = DebuggerParamType::REQUIRED); bool ParamBool(const char *name, bool *out, DebuggerParamType type = DebuggerParamType::REQUIRED); + bool ParamString(const char *name, std::string *out, DebuggerParamType type = DebuggerParamType::REQUIRED); JsonWriter &Respond(); void Finish(); From e746a2d10691c5b57c695c359344d03367468452 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 Apr 2018 22:02:04 -0700 Subject: [PATCH 187/301] Debugger: Add stepping to WebSocket API. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Debugger/WebSocket.cpp | 2 + Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 3 + .../Debugger/WebSocket/SteppingSubscriber.cpp | 224 ++++++++++++++++++ Core/Debugger/WebSocket/SteppingSubscriber.h | 23 ++ android/jni/Android.mk | 1 + 8 files changed, 263 insertions(+) create mode 100644 Core/Debugger/WebSocket/SteppingSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/SteppingSubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b61d39d89b..608d057425 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1416,6 +1416,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/LogBroadcaster.h Core/Debugger/WebSocket/SteppingBroadcaster.cpp Core/Debugger/WebSocket/SteppingBroadcaster.h + Core/Debugger/WebSocket/SteppingSubscriber.cpp + Core/Debugger/WebSocket/SteppingSubscriber.h Core/Debugger/WebSocket/WebSocketUtils.cpp Core/Debugger/WebSocket/WebSocketUtils.h Core/Dialog/PSPDialog.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d21c391324..c06e5f1e78 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -191,6 +191,7 @@ + @@ -543,6 +544,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index d9d65387df..796be8ac6d 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -719,6 +719,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1325,6 +1328,9 @@ Debugger\WebSocket + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 390dd4fa00..7dbb1b42b7 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -50,6 +50,7 @@ #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h" +#include "Core/Debugger/WebSocket/SteppingSubscriber.h" typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); typedef void (*Subscribershutdown)(void *p); @@ -62,6 +63,7 @@ static const std::vector subscribers({ { &WebSocketCPUCoreInit, nullptr }, { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, { &WebSocketGameInit, nullptr }, + { &WebSocketSteppingInit, &WebSocketSteppingShutdown }, }); // To handle webserver restart, keep track of how many running. diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 1b795899a7..4bba5df54e 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -71,6 +71,9 @@ void WebSocketCPUResume(DebuggerRequest &req) { return req.Fail("CPU not stepping"); } + if (currentMIPS->inDelaySlot) { + Core_DoSingleStep(); + } Core_EnableStepping(false); } diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp new file mode 100644 index 0000000000..3b3842353a --- /dev/null +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -0,0 +1,224 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "base/stringutil.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/WebSocket/SteppingSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/Core.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/sceKernelThread.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/MIPS/MIPSStackWalk.h" + +using namespace MIPSAnalyst; + +struct WebSocketSteppingState { + WebSocketSteppingState() { + disasm_.setCpu(currentDebugMIPS); + } + + void Into(DebuggerRequest &req); + void Over(DebuggerRequest &req); + void Out(DebuggerRequest &req); + void RunUntil(DebuggerRequest &req); + void HLE(DebuggerRequest &req); + +protected: + u32 GetNextAddress(); + int GetNextInstructionCount(); + void PrepareResume(); + + DisassemblyManager disasm_; +}; + +void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) { + auto p = new WebSocketSteppingState(); + map["cpu.stepInto"] = std::bind(&WebSocketSteppingState::Into, p, std::placeholders::_1); + map["cpu.stepOver"] = std::bind(&WebSocketSteppingState::Over, p, std::placeholders::_1); + map["cpu.stepOut"] = std::bind(&WebSocketSteppingState::Out, p, std::placeholders::_1); + map["cpu.runUntil"] = std::bind(&WebSocketSteppingState::RunUntil, p, std::placeholders::_1); + map["cpu.nextHLE"] = std::bind(&WebSocketSteppingState::HLE, p, std::placeholders::_1); + + return p; +} + +void WebSocketSteppingShutdown(void *p) { + delete static_cast(p); +} + +// Single step into the next instruction (cpu.stepInto) +// +// No parameters. +// +// No immediate response. A cpu.stepping event will be sent once complete. +void WebSocketSteppingState::Into(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + if (!Core_IsStepping()) { + Core_EnableStepping(true); + return; + } + + // If the current PC is on a breakpoint, the user doesn't want to do nothing. + CBreakPoints::SetSkipFirst(currentMIPS->pc); + + int c = GetNextInstructionCount(); + for (int i = 0; i < c; ++i) { + Core_DoSingleStep(); + } +} + +// Step over the next instruction (cpu.stepOver) +// +// Note: this jumps over function calls, but also delay slots. +// +// No parameters. +// +// No immediate response. A cpu.stepping event will be sent once complete. +void WebSocketSteppingState::Over(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + MipsOpcodeInfo info = GetOpcodeInfo(currentDebugMIPS, currentMIPS->pc); + u32 breakpointAddress = GetNextAddress(); + if (info.isBranch) { + if (info.isConditional && !info.isLinkedBranch) { + if (info.conditionMet) { + breakpointAddress = info.branchTarget; + } else { + // Skip over the delay slot. + breakpointAddress += 4; + } + } else { + if (info.isLinkedBranch) { + // jal or jalr - a function call. Skip the delay slot. + breakpointAddress += 4; + } else { + // j - for absolute branches, set the breakpoint at the branch target. + breakpointAddress = info.branchTarget; + } + } + } + + PrepareResume(); + // Could have advanced to the breakpoint already in PrepareResume(). + if (currentMIPS->pc != breakpointAddress) { + CBreakPoints::AddBreakPoint(breakpointAddress, true); + Core_EnableStepping(false); + } +} + +// Step out of a function based on a stack walk (cpu.stepOut) +// +// No parameters. +// +// No immediate response. A cpu.stepping event will be sent once complete. +void WebSocketSteppingState::Out(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + auto threads = GetThreadsInfo(); + u32 entry = currentMIPS->pc; + u32 stackTop = 0; + for (const DebugThreadInfo &th : threads) { + if (th.isCurrent) { + entry = th.entrypoint; + stackTop = th.initialStack; + break; + } + } + + auto frames = MIPSStackWalk::Walk(currentMIPS->pc, currentMIPS->r[MIPS_REG_RA], currentMIPS->r[MIPS_REG_SP], entry, stackTop); + if (frames.size() < 2) { + // TODO: Respond in some way? + return; + } + + u32 breakpointAddress = frames[1].pc; + PrepareResume(); + // Could have advanced to the breakpoint already in PrepareResume(). + if (currentMIPS->pc != breakpointAddress) { + CBreakPoints::AddBreakPoint(breakpointAddress, true); + Core_EnableStepping(false); + } +} + +// Run until a certain address (cpu.stepOut) +// +// Parameters: +// - address: number parameter for destination. +// +// No immediate response. A cpu.stepping event will be sent once complete. +void WebSocketSteppingState::RunUntil(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + u32 address = 0; + if (!req.ParamU32("address", &address)) { + // Error already sent. + return; + } + + bool wasAtAddress = currentMIPS->pc == address; + PrepareResume(); + // We may have arrived already if PauseResume() stepped out of a delay slot. + if (currentMIPS->pc != address || wasAtAddress) { + CBreakPoints::AddBreakPoint(address, true); + Core_EnableStepping(false); + } +} + +// Jump after the next HLE call (cpu.nextHLE) +// +// No parameters. +// +// No immediate response. A cpu.stepping event will be sent once complete. +void WebSocketSteppingState::HLE(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + PrepareResume(); + hleDebugBreak(); + Core_EnableStepping(false); +} + +u32 WebSocketSteppingState::GetNextAddress() { + u32 current = disasm_.getStartAddress(currentMIPS->pc); + return disasm_.getNthNextAddress(current, 1); +} + +int WebSocketSteppingState::GetNextInstructionCount() { + return (GetNextAddress() - currentMIPS->pc) / 4; +} + +void WebSocketSteppingState::PrepareResume() { + if (currentMIPS->inDelaySlot) { + Core_DoSingleStep(); + } else { + // If the current PC is on a breakpoint, the user doesn't want to do nothing. + CBreakPoints::SetSkipFirst(currentMIPS->pc); + } +} + diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.h b/Core/Debugger/WebSocket/SteppingSubscriber.h new file mode 100644 index 0000000000..319c608caa --- /dev/null +++ b/Core/Debugger/WebSocket/SteppingSubscriber.h @@ -0,0 +1,23 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketSteppingInit(DebuggerEventHandlerMap &map); +void WebSocketSteppingShutdown(void *p); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index de838dc453..35ee9c930e 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -308,6 +308,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/SteppingSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ From ccea863f00dbcf44ca6c4191a0af39694a0e48f2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 19:21:45 -0700 Subject: [PATCH 188/301] Debugger: Use fragments for long chunks. Gets it on the wire faster. More importantly, this allows us to usually avoid rampant memory allocation even for large responses. --- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 4 ++++ Core/Debugger/WebSocket/WebSocketUtils.cpp | 11 ++++++++++- Core/Debugger/WebSocket/WebSocketUtils.h | 2 ++ ext/native/json/json_writer.cpp | 4 ++-- ext/native/json/json_writer.h | 6 ++++++ ext/native/net/websocket_server.cpp | 11 +++++++++++ ext/native/net/websocket_server.h | 1 + 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 25e15bec2f..f3507aa34a 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -290,6 +290,10 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { disasm_.getLine(addr, displaySymbols, line); WriteDisasmLine(json, line); addr += line.totalSize; + + // These are pretty long, so let's grease the wheels a bit. + if (i % 50 == 0) + req.Flush(); } json.pop(); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index d21b93e9d3..fe6e343b0a 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -32,12 +32,21 @@ JsonWriter &DebuggerRequest::Respond() { void DebuggerRequest::Finish() { if (responseBegun_ && !responseSent_) { writer_.end(); - ws->Send(writer_.str()); + if (responsePartial_) + ws->AddFragment(true, writer_.str()); + else + ws->Send(writer_.str()); responseBegun_ = false; responseSent_ = true; + responsePartial_ = false; } } +void DebuggerRequest::Flush() { + ws->AddFragment(false, writer_.flush()); + responsePartial_ = true; +} + static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) { if (TryParse(str, out)) return true; diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 1391e6614c..6bad56e5b9 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -85,12 +85,14 @@ struct DebuggerRequest { bool ParamString(const char *name, std::string *out, DebuggerParamType type = DebuggerParamType::REQUIRED); JsonWriter &Respond(); + void Flush(); void Finish(); private: JsonWriter writer_; bool responseBegun_ = false; bool responseSent_ = false; + bool responsePartial_ = false; }; typedef std::function DebuggerEventHandler; diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 3cc625e13d..15defaac84 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -72,7 +72,7 @@ const char *JsonWriter::comma() const { const char *JsonWriter::arrayComma() const { if (stack_.back().first) { - return "\n"; + return pretty_ ? "\n" : ""; } else { return pretty_ ? ", " : ","; } @@ -159,7 +159,7 @@ void JsonWriter::writeString(const char *value) { void JsonWriter::writeString(const char *name, const char *value) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": \""; + str_ << (pretty_ ? "\": \"" : "\":\""); writeEscapedString(value); str_ << "\""; stack_.back().first = false; diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index 06afb66bc7..ee63708fb4 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -59,6 +59,12 @@ public: return str_.str(); } + std::string flush() { + std::string result = str_.str(); + str_.str(""); + return result; + } + enum { NORMAL = 0, PRETTY = 1, diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index 0302ddbb22..40cadfb733 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -46,6 +46,8 @@ enum class Opcode { CONTROL_MAX = 10, }; +static const size_t OUT_PRESSURE = 65536; + static inline std::string TrimString(const std::string &s) { auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) { // isspace() expects 0 - 255, so convert any sign-extended value. @@ -508,6 +510,14 @@ void WebSocketServer::SendBytes(const void *p, size_t sz) { size_t pos = outBuf_.size(); outBuf_.resize(pos + sz); memcpy(&outBuf_[pos], data, sz); + + if (pos + sz > lastPressure_ + OUT_PRESSURE) { + size_t pushed = out_->PushAtMost((const char *)&outBuf_[0], outBuf_.size()); + if (pushed != 0) { + outBuf_.erase(outBuf_.begin(), outBuf_.begin() + pushed); + } + lastPressure_ = outBuf_.size(); + } } } @@ -529,6 +539,7 @@ void WebSocketServer::SendFlush() { // Hopefully this is usually the entire buffer. outBuf_.erase(outBuf_.begin(), outBuf_.begin() + totalPushed); } + lastPressure_ = outBuf_.size(); } }; diff --git a/ext/native/net/websocket_server.h b/ext/native/net/websocket_server.h index 98426f53cf..2c4c59739e 100644 --- a/ext/native/net/websocket_server.h +++ b/ext/native/net/websocket_server.h @@ -86,6 +86,7 @@ protected: OutputSink *out_ = nullptr; WebSocketClose closeReason_ = WebSocketClose::NO_STATUS; std::vector outBuf_; + size_t lastPressure_ = 0; std::vector pendingBuf_; uint8_t pendingMask_[4]{}; From fc8ad3b47b77be43ab569845b93861d8c878a11f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 20:38:39 -0700 Subject: [PATCH 189/301] json: Optimize writing a bit. This improves the responsiveness of certain APIs. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 16 ++-- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 58 ++++++------ Core/Debugger/WebSocket/GameBroadcaster.cpp | 2 +- Core/Debugger/WebSocket/GameSubscriber.cpp | 2 +- .../WebSocket/SteppingBroadcaster.cpp | 2 +- ext/native/json/json_writer.cpp | 90 ++++++++++++++----- ext/native/json/json_writer.h | 42 +++------ 7 files changed, 121 insertions(+), 91 deletions(-) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 4bba5df54e..97092a710c 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -91,7 +91,7 @@ void WebSocketCPUStatus(DebuggerRequest &req) { json.writeBool("stepping", PSP_IsInited() && Core_IsStepping() && coreState != CORE_POWERDOWN); json.writeBool("paused", GetUIState() != UISTATE_INGAME); // Avoid NULL deference. - json.writeFloat("pc", PSP_IsInited() ? currentMIPS->pc : 0); + json.writeUint("pc", PSP_IsInited() ? currentMIPS->pc : 0); // A double ought to be good enough for a 156 day debug session. json.writeFloat("ticks", PSP_IsInited() ? CoreTiming::GetTicks() : 0); } @@ -131,11 +131,11 @@ void WebSocketCPUGetAllRegs(DebuggerRequest &req) { json.pushArray("uintValues"); // Writing as floating point to avoid negatives. Actually double, so safe. for (int r = 0; r < total; ++r) - json.writeFloat(currentDebugMIPS->GetRegValue(c, r)); + json.writeUint(currentDebugMIPS->GetRegValue(c, r)); if (c == 0) { - json.writeFloat(currentDebugMIPS->GetPC()); - json.writeFloat(currentDebugMIPS->GetHi()); - json.writeFloat(currentDebugMIPS->GetLo()); + json.writeUint(currentDebugMIPS->GetPC()); + json.writeUint(currentDebugMIPS->GetHi()); + json.writeUint(currentDebugMIPS->GetLo()); } json.pop(); @@ -267,7 +267,7 @@ void WebSocketCPUGetReg(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeInt("category", cat); json.writeInt("register", reg); - json.writeFloat("uintValue", val); + json.writeUint("uintValue", val); json.writeString("floatValue", RegValueAsFloat(val)); } @@ -335,7 +335,7 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { // Repeat it back just to avoid confusion on how it parsed. json.writeInt("category", cat); json.writeInt("register", reg); - json.writeFloat("uintValue", val); + json.writeUint("uintValue", val); json.writeString("floatValue", RegValueAsFloat(val)); } @@ -368,6 +368,6 @@ void WebSocketCPUEvaluate(DebuggerRequest &req) { } JsonWriter &json = req.Respond(); - json.writeFloat("uintValue", val); + json.writeUint("uintValue", val); json.writeString("floatValue", RegValueAsFloat(val)); } diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index f3507aa34a..676d0a21f5 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -63,17 +63,17 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi else if (l.type == DISTYPE_OTHER) json.writeString("type", "other"); - json.writeFloat("address", addr); + json.writeUint("address", addr); json.writeInt("addressSize", l.totalSize); - json.writeFloat("encoding", Memory::IsValidAddress(addr) ? Memory::Read_Instruction(addr).encoding : 0); + json.writeUint("encoding", Memory::IsValidAddress(addr) ? Memory::Read_Instruction(addr).encoding : 0); if (l.totalSize >= 8 && Memory::IsValidRange(addr, l.totalSize)) { json.pushArray("macroEncoding"); for (u32 off = 0; off < l.totalSize; off += 4) { - json.writeFloat(Memory::Read_Instruction(addr + off).encoding); + json.writeUint(Memory::Read_Instruction(addr + off).encoding); } json.pop(); } else { - json.writeRaw("macroEncoding", "null"); + json.writeNull("macroEncoding"); } int c = currentDebugMIPS->getColor(addr) & 0x00FFFFFF; json.writeString("backgroundColor", StringFromFormat("#%02x%02x%02x", c & 0xFF, (c >> 8) & 0xFF, c >> 16)); @@ -82,7 +82,7 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi const std::string addressSymbol = g_symbolMap->GetLabelString(addr); if (addressSymbol.empty()) - json.writeRaw("symbol", "null"); + json.writeNull("symbol"); else json.writeString("symbol", addressSymbol); @@ -95,10 +95,10 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi if (cond) json.writeString("expression", cond->expressionString); else - json.writeRaw("expression", "null"); + json.writeNull("expression"); json.pop(); } else { - json.writeRaw("breakpoint", "null"); + json.writeNull("breakpoint"); } json.writeBool("isCurrentPC", currentDebugMIPS->GetPC() == addr); @@ -107,70 +107,70 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi std::string targetSymbol; if (!l.info.isBranchToRegister) { targetSymbol = g_symbolMap->GetLabelString(l.info.branchTarget); - json.writeFloat("targetAddress", l.info.branchTarget); - json.writeRaw("register", "null"); + json.writeUint("targetAddress", l.info.branchTarget); + json.writeNull("register"); } else { - json.writeRaw("targetAddress", "null"); + json.writeNull("targetAddress"); json.writeInt("register", l.info.branchRegisterNum); } json.writeBool("isLinked", l.info.isLinkedBranch); json.writeBool("isLikely", l.info.isLikelyBranch); if (targetSymbol.empty()) - json.writeRaw("symbol", "null"); + json.writeNull("symbol"); else json.writeString("symbol", targetSymbol); json.pop(); } else { - json.writeRaw("branch", "null"); + json.writeNull("branch"); } if (l.info.hasRelevantAddress) { json.pushDict("relevantData"); - json.writeFloat("address", l.info.relevantAddress); + json.writeUint("address", l.info.relevantAddress); if (Memory::IsValidRange(l.info.relevantAddress, 4)) - json.writeFloat("uintValue", Memory::ReadUnchecked_U32(l.info.relevantAddress)); + json.writeUint("uintValue", Memory::ReadUnchecked_U32(l.info.relevantAddress)); else - json.writeRaw("uintValue", "null"); + json.writeNull("uintValue"); json.pop(); } else { - json.writeRaw("relevantData", "null"); + json.writeNull("relevantData"); } if (l.info.isConditional) json.writeBool("conditionMet", l.info.conditionMet); else - json.writeRaw("conditionMet", "null"); + json.writeNull("conditionMet"); if (l.info.isDataAccess) { json.pushDict("dataAccess"); - json.writeFloat("address", l.info.dataAddress); + json.writeUint("address", l.info.dataAddress); json.writeInt("size", l.info.dataSize); std::string dataSymbol = g_symbolMap->GetLabelString(l.info.dataAddress); std::string valueSymbol; if (!Memory::IsValidRange(l.info.dataAddress, l.info.dataSize)) - json.writeRaw("uintValue", "null"); + json.writeNull("uintValue"); else if (l.info.dataSize == 1) - json.writeFloat("uintValue", Memory::ReadUnchecked_U8(l.info.dataAddress)); + json.writeUint("uintValue", Memory::ReadUnchecked_U8(l.info.dataAddress)); else if (l.info.dataSize == 2) - json.writeFloat("uintValue", Memory::ReadUnchecked_U16(l.info.dataAddress)); + json.writeUint("uintValue", Memory::ReadUnchecked_U16(l.info.dataAddress)); else if (l.info.dataSize >= 4) { u32 data = Memory::ReadUnchecked_U32(l.info.dataAddress); valueSymbol = g_symbolMap->GetLabelString(data); - json.writeFloat("uintValue", data); + json.writeUint("uintValue", data); } if (!dataSymbol.empty()) json.writeString("symbol", dataSymbol); else - json.writeRaw("symbol", "null"); + json.writeNull("symbol"); if (!valueSymbol.empty()) json.writeString("valueSymbol", valueSymbol); else - json.writeRaw("valueSymbol", "null"); + json.writeNull("valueSymbol"); json.pop(); } else { - json.writeRaw("dataAccess", "null"); + json.writeNull("dataAccess"); } json.pop(); @@ -178,8 +178,8 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi void WebSocketDisasmState::WriteBranchGuide(JsonWriter &json, const BranchLine &l) { json.pushDict(); - json.writeFloat("top", l.first); - json.writeFloat("bottom", l.second); + json.writeUint("top", l.first); + json.writeUint("bottom", l.second); if (l.type == LINE_UP) json.writeString("direction", "up"); else if (l.type == LINE_DOWN) @@ -279,8 +279,8 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.pushDict("range"); - json.writeFloat("start", start); - json.writeFloat("end", end); + json.writeUint("start", start); + json.writeUint("end", end); json.pop(); json.pushArray("lines"); diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp index 4c53522a98..3045e70fc4 100644 --- a/Core/Debugger/WebSocket/GameBroadcaster.cpp +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -34,7 +34,7 @@ struct GameStatusEvent { j.writeString("title", g_paramSFO.GetValueString("TITLE")); j.pop(); } else { - j.writeRaw("game", "null"); + j.writeNull("game"); } j.end(); return j.str(); diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp index e2241ac04e..a17717e262 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.cpp +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -46,7 +46,7 @@ void WebSocketGameStatus(DebuggerRequest &req) { json.writeString("title", g_paramSFO.GetValueString("TITLE")); json.pop(); } else { - json.writeRaw("game", "null"); + json.writeNull("game"); } json.writeBool("paused", GetUIState() == UISTATE_PAUSEMENU); } diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp index 9b9cbf3200..bde51e1f2f 100644 --- a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -27,7 +27,7 @@ struct CPUSteppingEvent { JsonWriter j; j.begin(); j.writeString("event", "cpu.stepping"); - j.writeFloat("pc", currentMIPS->pc); + j.writeUint("pc", currentMIPS->pc); // A double ought to be good enough for a 156 day debug session. j.writeFloat("ticks", CoreTiming::GetTicks()); j.end(); diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 15defaac84..7d1c47a620 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -84,10 +84,10 @@ void JsonWriter::pushDict() { stack_.push_back(StackEntry(DICT)); } -void JsonWriter::pushDict(const char *name) { +void JsonWriter::pushDict(const std::string &name) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": {"; + str_ << (pretty_ ? "\": {" : "\":{"); stack_.back().first = false; stack_.push_back(StackEntry(DICT)); } @@ -98,10 +98,10 @@ void JsonWriter::pushArray() { stack_.push_back(StackEntry(ARRAY)); } -void JsonWriter::pushArray(const char *name) { +void JsonWriter::pushArray(const std::string &name) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": ["; + str_ << (pretty_ ? "\": [" : "\":["); stack_.push_back(StackEntry(ARRAY)); } @@ -110,10 +110,10 @@ void JsonWriter::writeBool(bool value) { stack_.back().first = false; } -void JsonWriter::writeBool(const char *name, bool value) { +void JsonWriter::writeBool(const std::string &name, bool value) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": " << (value ? "true" : "false"); + str_ << (pretty_ ? "\": " : "\":") << (value ? "true" : "false"); stack_.back().first = false; } @@ -122,10 +122,22 @@ void JsonWriter::writeInt(int value) { stack_.back().first = false; } -void JsonWriter::writeInt(const char *name, int value) { +void JsonWriter::writeInt(const std::string &name, int value) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": " << value; + str_ << (pretty_ ? "\": " : "\":") << value; + stack_.back().first = false; +} + +void JsonWriter::writeUint(uint32_t value) { + str_ << arrayComma() << arrayIndent() << value; + stack_.back().first = false; +} + +void JsonWriter::writeUint(const std::string &name, uint32_t value) { + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << (pretty_ ? "\": " : "\":") << value; stack_.back().first = false; } @@ -138,10 +150,10 @@ void JsonWriter::writeFloat(double value) { stack_.back().first = false; } -void JsonWriter::writeFloat(const char *name, double value) { +void JsonWriter::writeFloat(const std::string &name, double value) { str_ << comma() << indent() << "\""; writeEscapedString(name); - str_ << "\": "; + str_ << (pretty_ ? "\": " : "\":"); if (std::isfinite(value)) str_ << value; else @@ -149,14 +161,14 @@ void JsonWriter::writeFloat(const char *name, double value) { stack_.back().first = false; } -void JsonWriter::writeString(const char *value) { +void JsonWriter::writeString(const std::string &value) { str_ << arrayComma() << arrayIndent() << "\""; writeEscapedString(value); str_ << "\""; stack_.back().first = false; } -void JsonWriter::writeString(const char *name, const char *value) { +void JsonWriter::writeString(const std::string &name, const std::string &value) { str_ << comma() << indent() << "\""; writeEscapedString(name); str_ << (pretty_ ? "\": \"" : "\":\""); @@ -165,12 +177,12 @@ void JsonWriter::writeString(const char *name, const char *value) { stack_.back().first = false; } -void JsonWriter::writeRaw(const char *value) { +void JsonWriter::writeRaw(const std::string &value) { str_ << arrayComma() << arrayIndent() << value; stack_.back().first = false; } -void JsonWriter::writeRaw(const char *name, const char *value) { +void JsonWriter::writeRaw(const std::string &name, const std::string &value) { str_ << comma() << indent() << "\""; writeEscapedString(name); str_ << (pretty_ ? "\": " : "\":"); @@ -178,6 +190,18 @@ void JsonWriter::writeRaw(const char *name, const char *value) { stack_.back().first = false; } +void JsonWriter::writeNull() { + str_ << arrayComma() << arrayIndent() << "null"; + stack_.back().first = false; +} + +void JsonWriter::writeNull(const std::string &name) { + str_ << comma() << indent() << "\""; + writeEscapedString(name); + str_ << (pretty_ ? "\": " : "\":") << "null"; + stack_.back().first = false; +} + void JsonWriter::pop() { BlockType type = stack_.back().type; stack_.pop_back(); @@ -197,33 +221,53 @@ void JsonWriter::pop() { stack_.back().first = false; } -void JsonWriter::writeEscapedString(const char *str) { +void JsonWriter::writeEscapedString(const std::string &str) { size_t pos = 0; - size_t len = strlen(str); + const size_t len = str.size(); auto update = [&](size_t current, size_t skip = 0) { size_t end = current; if (pos < end) - str_ << std::string(str + pos, end - pos); + str_ << str.substr(pos, end - pos); pos = end + skip; }; for (size_t i = 0; i < len; ++i) { - if (str[i] == '\\' || str[i] == '"' || str[i] == '/') { + switch (str[i]) { + case '\\': + case '"': + case '/': update(i); str_ << '\\'; - } else if (str[i] == '\r') { + break; + + case '\r': update(i, 1); str_ << "\\r"; - } else if (str[i] == '\n') { + break; + break; + + case '\n': update(i, 1); str_ << "\\n"; - } else if (str[i] == '\t') { + break; + break; + + case '\t': update(i, 1); str_ << "\\t"; - } else if (str[i] < 32) { + break; + + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 11: + case 12: case 14: case 15: case 16: case 17: case 18: case 19: case 20: + case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: + case 29: case 30: case 31: update(i, 1); str_ << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int)str[i] << std::dec << std::setw(0); + break; + + default: + break; } } @@ -273,7 +317,7 @@ std::string json_stringify(const JsonNode *node) { static void json_stringify_object(JsonWriter &writer, const JsonNode *node) { switch (node->value.getTag()) { case JSON_NULL: - writer.writeRaw(node->key, "null"); + writer.writeNull(node->key); break; case JSON_STRING: writer.writeString(node->key, node->value.toString()); diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index ee63708fb4..22c712c856 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -22,38 +22,24 @@ public: void beginRaw(); void end(); void pushDict(); - void pushDict(const char *name); + void pushDict(const std::string &name); void pushArray(); - void pushArray(const char *name); + void pushArray(const std::string &name); void pop(); void writeBool(bool value); - void writeBool(const char *name, bool value); + void writeBool(const std::string &name, bool value); void writeInt(int value); - void writeInt(const char *name, int value); + void writeInt(const std::string &name, int value); + void writeUint(uint32_t value); + void writeUint(const std::string &name, uint32_t value); void writeFloat(double value); - void writeFloat(const char *name, double value); - void writeString(const char *value); - void writeString(const char *name, const char *value); - void writeString(const std::string &value) { - writeString(value.c_str()); - } - void writeString(const char *name, const std::string &value) { - writeString(name, value.c_str()); - } - void writeString(const std::string &name, const std::string &value) { - writeString(name.c_str(), value.c_str()); - } - void writeRaw(const char *value); - void writeRaw(const char *name, const char *value); - void writeRaw(const std::string &value) { - writeRaw(value.c_str()); - } - void writeRaw(const char *name, const std::string &value) { - writeRaw(name, value.c_str()); - } - void writeRaw(const std::string &name, const std::string &value) { - writeRaw(name.c_str(), value.c_str()); - } + void writeFloat(const std::string &name, double value); + void writeString(const std::string &value); + void writeString(const std::string &name, const std::string &value); + void writeRaw(const std::string &value); + void writeRaw(const std::string &name, const std::string &value); + void writeNull(); + void writeNull(const std::string &name); std::string str() const { return str_.str(); @@ -76,7 +62,7 @@ private: const char *arrayComma() const; const char *indent() const; const char *arrayIndent() const; - void writeEscapedString(const char *s); + void writeEscapedString(const std::string &s); enum BlockType { ARRAY, From 2f3b6c19d006f4d2f58c456466f07c88dc56de42 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 30 Apr 2018 20:39:18 -0700 Subject: [PATCH 190/301] Debugger: Poll frequently after stepping. This will make us listen for events slightly less often, so we don't want to sustain it. --- Core/Debugger/WebSocket.cpp | 12 ++++++++++-- Core/Debugger/WebSocket/WebSocketUtils.cpp | 4 +++- Core/Debugger/WebSocket/WebSocketUtils.h | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 7dbb1b42b7..dc7d44c59b 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -129,6 +129,8 @@ void HandleDebuggerRequest(const http::Request &request) { subscriberData.push_back(info.init(eventHandlers)); } + // There's a tradeoff between responsiveness to incoming events, and polling for changes. + int highActivity = 0; ws->SetTextHandler([&](const std::string &t) { JsonReader reader(t.c_str(), t.size()); if (!reader.ok()) { @@ -148,7 +150,10 @@ void HandleDebuggerRequest(const http::Request &request) { if (eventFunc != eventHandlers.end()) { std::lock_guard guard(lifecycleLock); eventFunc->second(req); - req.Finish(); + if (!req.Finish()) { + // Poll more frequently for a second in case this triggers something. + highActivity = 1000; + } } else { req.Fail("Bad message: unknown event"); } @@ -157,7 +162,7 @@ void HandleDebuggerRequest(const http::Request &request) { ws->Send(DebuggerErrorEvent("Bad message", LogTypes::LERROR)); }); - while (ws->Process(1.0f / 60.0f)) { + while (ws->Process(highActivity ? 1.0f / 1000.0f : 1.0f / 60.0f)) { std::lock_guard guard(lifecycleLock); // These send events that aren't just responses to requests. logger.Broadcast(ws); @@ -167,6 +172,9 @@ void HandleDebuggerRequest(const http::Request &request) { if (stopRequested) { ws->Close(net::WebSocketClose::GOING_AWAY); } + if (highActivity > 0) { + highActivity--; + } } std::lock_guard guard(lifecycleLock); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index fe6e343b0a..54761aaa55 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -29,7 +29,7 @@ JsonWriter &DebuggerRequest::Respond() { return writer_; } -void DebuggerRequest::Finish() { +bool DebuggerRequest::Finish() { if (responseBegun_ && !responseSent_) { writer_.end(); if (responsePartial_) @@ -40,6 +40,8 @@ void DebuggerRequest::Finish() { responseSent_ = true; responsePartial_ = false; } + + return responseSent_; } void DebuggerRequest::Flush() { diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 6bad56e5b9..807d8e3f10 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -86,7 +86,7 @@ struct DebuggerRequest { JsonWriter &Respond(); void Flush(); - void Finish(); + bool Finish(); private: JsonWriter writer_; From 65feb5f09ce2e86603bc2e796c81d5b4f613d1b2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 1 May 2018 20:17:12 -0700 Subject: [PATCH 191/301] Debugger: Include assembler API. --- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 2 +- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 97092a710c..d08e2b2060 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -271,7 +271,7 @@ void WebSocketCPUGetReg(DebuggerRequest &req) { json.writeString("floatValue", RegValueAsFloat(val)); } -// Retrieve the value of a single register (cpu.getReg) +// Update the value of a single register (cpu.setReg) // // Parameters (by name): // - name: string name of register to lookup. diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 676d0a21f5..133f845bcd 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -17,11 +17,13 @@ #include #include "base/stringutil.h" +#include "util/text/utf8.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/MemMap.h" +#include "Core/MIPS/MIPSAsm.h" #include "Core/MIPS/MIPSDebugInterface.h" struct WebSocketDisasmState { @@ -31,6 +33,7 @@ struct WebSocketDisasmState { void Base(DebuggerRequest &req); void Disasm(DebuggerRequest &req); + void Assemble(DebuggerRequest &req); protected: void WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l); @@ -43,6 +46,7 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) { auto p = new WebSocketDisasmState(); map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1); map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1); + map["memory.assemble"] = std::bind(&WebSocketDisasmState::Assemble, p, std::placeholders::_1); return p; } @@ -285,8 +289,8 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { json.pushArray("lines"); DisassemblyLineInfo line; - u32 addr = start; - for (u32 i = 0; i < count; ++i) { + uint32_t addr = start; + for (uint32_t i = 0; i < count; ++i) { disasm_.getLine(addr, displaySymbols, line); WriteDisasmLine(json, line); addr += line.totalSize; @@ -303,3 +307,22 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { WriteBranchGuide(json, bl); json.pop(); } + +void WebSocketDisasmState::Assemble(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { + return req.Fail("CPU not started"); + } + + uint32_t address; + if (!req.ParamU32("address", &address)) + return; + std::string code; + if (!req.ParamString("code", &code)) + return; + + if (!MIPSAsm::MipsAssembleOpcode(code.c_str(), currentDebugMIPS, address)) + return req.Fail(StringFromFormat("Could not assemble: %s", ConvertWStringToUTF8(MIPSAsm::GetAssembleError()).c_str())); + + JsonWriter &json = req.Respond(); + json.writeUint("encoding", Memory::Read_Instruction(address).encoding); +} From 5b132c904ad044f01917efbc39681919495f930a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 1 May 2018 22:26:21 -0700 Subject: [PATCH 192/301] Debugger: Fix resume not skipping breakpoints. --- Core/Debugger/DisassemblyManager.cpp | 3 ++- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 77e8c917a5..660ce2aa89 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -524,7 +524,8 @@ void DisassemblyFunction::generateBranchLines() if (lane == -1) { - // error + // Let's just pile on. + lines[i].laneIndex = 15; continue; } diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index d08e2b2060..7b1ffb68ac 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -18,6 +18,7 @@ #include "Common/StringUtils.h" #include "Core/Core.h" #include "Core/CoreTiming.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/MIPS/MIPS.h" @@ -71,6 +72,7 @@ void WebSocketCPUResume(DebuggerRequest &req) { return req.Fail("CPU not stepping"); } + CBreakPoints::SetSkipFirst(currentMIPS->pc); if (currentMIPS->inDelaySlot) { Core_DoSingleStep(); } From 6cf24b34fc1edaaf0511455eec8ceae1a52fd0d3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 1 May 2018 22:59:50 -0700 Subject: [PATCH 193/301] Debugger: Add thread safety to breakpoints. --- Core/Debugger/Breakpoints.cpp | 162 ++++++++++++++++++++++++++-------- Core/Debugger/Breakpoints.h | 10 ++- 2 files changed, 133 insertions(+), 39 deletions(-) diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index 0223a2f6fc..c5515f4f34 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "Common/Log.h" #include "Core/Core.h" @@ -27,9 +28,11 @@ #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/CoreTiming.h" +static std::mutex breakPointsMutex_; std::vector CBreakPoints::breakPoints_; u32 CBreakPoints::breakSkipFirstAt_ = 0; u64 CBreakPoints::breakSkipFirstTicks_ = 0; +static std::mutex memCheckMutex_; std::vector CBreakPoints::memChecks_; std::vector CBreakPoints::cleanupMemChecks_; @@ -45,16 +48,21 @@ void MemCheck::Log(u32 addr, bool write, int size, u32 pc) { } } -BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc) -{ +BreakAction MemCheck::Apply(u32 addr, bool write, int size, u32 pc) { int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ; - if (cond & mask) - { + if (cond & mask) { ++numHits; + return result; + } + return BREAK_ACTION_IGNORE; +} + +BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc) { + int mask = write ? MEMCHECK_WRITE : MEMCHECK_READ; + if (cond & mask) { Log(addr, write, size, pc); - if (result & BREAK_ACTION_PAUSE) - { + if (result & BREAK_ACTION_PAUSE) { Core_EnableStepping(true); host->SetDebugMode(true); } @@ -65,38 +73,46 @@ BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc) return BREAK_ACTION_IGNORE; } -void MemCheck::JitBefore(u32 addr, bool write, int size, u32 pc) -{ +void MemCheck::JitBeforeApply(u32 addr, bool write, int size, u32 pc) { int mask = MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE; - if (write && (cond & mask) == mask) - { + if (write && (cond & mask) == mask) { lastAddr = addr; lastPC = pc; lastSize = size; + } else { + lastAddr = 0; + Apply(addr, write, size, pc); + } +} +void MemCheck::JitBeforeAction(u32 addr, bool write, int size, u32 pc) { + if (lastAddr) { // We have to break to find out if it changed. Core_EnableStepping(true); - } - else - { - lastAddr = 0; + } else { Action(addr, write, size, pc); } } -void MemCheck::JitCleanup() -{ +bool MemCheck::JitApplyChanged() { if (lastAddr == 0 || lastPC == 0) - return; + return false; // Here's the tricky part: would this have changed memory? // Note that it did not actually get written. bool changed = MIPSAnalyst::OpWouldChangeMemory(lastPC, lastAddr, lastSize); if (changed) - { ++numHits; + return changed; +} + +void MemCheck::JitCleanup(bool changed) +{ + if (lastAddr == 0 || lastPC == 0) + return; + + if (changed) Log(lastAddr, true, lastSize, lastPC); - } // Resume if it should not have gone to stepping, or if it did not change. if ((!(result & BREAK_ACTION_PAUSE) || !changed) && coreState == CORE_STEPPING) @@ -108,6 +124,7 @@ void MemCheck::JitCleanup() host->SetDebugMode(true); } +// Note: must lock while calling this. size_t CBreakPoints::FindBreakpoint(u32 addr, bool matchTemp, bool temp) { size_t found = INVALID_BREAKPOINT; @@ -140,12 +157,14 @@ size_t CBreakPoints::FindMemCheck(u32 start, u32 end) bool CBreakPoints::IsAddressBreakPoint(u32 addr) { + std::lock_guard guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr); return bp != INVALID_BREAKPOINT && breakPoints_[bp].result != BREAK_ACTION_IGNORE; } bool CBreakPoints::IsAddressBreakPoint(u32 addr, bool* enabled) { + std::lock_guard guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr); if (bp == INVALID_BREAKPOINT) return false; if (enabled != nullptr) @@ -155,12 +174,14 @@ bool CBreakPoints::IsAddressBreakPoint(u32 addr, bool* enabled) bool CBreakPoints::IsTempBreakPoint(u32 addr) { + std::lock_guard guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, true, true); return bp != INVALID_BREAKPOINT; } bool CBreakPoints::RangeContainsBreakPoint(u32 addr, u32 size) { + std::lock_guard guard(breakPointsMutex_); const u32 end = addr + size; for (const auto &bp : breakPoints_) { @@ -173,6 +194,7 @@ bool CBreakPoints::RangeContainsBreakPoint(u32 addr, u32 size) void CBreakPoints::AddBreakPoint(u32 addr, bool temp) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, true, temp); if (bp == INVALID_BREAKPOINT) { @@ -182,18 +204,21 @@ void CBreakPoints::AddBreakPoint(u32 addr, bool temp) pt.addr = addr; breakPoints_.push_back(pt); + guard.unlock(); Update(addr); } else if (!breakPoints_[bp].IsEnabled()) { breakPoints_[bp].result |= BREAK_ACTION_PAUSE; breakPoints_[bp].hasCond = false; + guard.unlock(); Update(addr); } } void CBreakPoints::RemoveBreakPoint(u32 addr) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr); if (bp != INVALID_BREAKPOINT) { @@ -204,12 +229,14 @@ void CBreakPoints::RemoveBreakPoint(u32 addr) if (bp != INVALID_BREAKPOINT) breakPoints_.erase(breakPoints_.begin() + bp); + guard.unlock(); Update(addr); } } void CBreakPoints::ChangeBreakPoint(u32 addr, bool status) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr); if (bp != INVALID_BREAKPOINT) { @@ -217,31 +244,38 @@ void CBreakPoints::ChangeBreakPoint(u32 addr, bool status) breakPoints_[bp].result |= BREAK_ACTION_PAUSE; else breakPoints_[bp].result = BreakAction(breakPoints_[bp].result & ~BREAK_ACTION_PAUSE); + + guard.unlock(); Update(addr); } } void CBreakPoints::ChangeBreakPoint(u32 addr, BreakAction result) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr); if (bp != INVALID_BREAKPOINT) { breakPoints_[bp].result = result; + guard.unlock(); Update(addr); } } void CBreakPoints::ClearAllBreakPoints() { + std::unique_lock guard(breakPointsMutex_); if (!breakPoints_.empty()) { breakPoints_.clear(); + guard.unlock(); Update(); } } void CBreakPoints::ClearTemporaryBreakPoints() { + std::unique_lock guard(breakPointsMutex_); if (breakPoints_.empty()) return; @@ -254,34 +288,40 @@ void CBreakPoints::ClearTemporaryBreakPoints() update = true; } } - + + guard.unlock(); if (update) Update(); } void CBreakPoints::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, true, false); if (bp != INVALID_BREAKPOINT) { breakPoints_[bp].hasCond = true; breakPoints_[bp].cond = cond; + guard.unlock(); Update(addr); } } void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, true, false); if (bp != INVALID_BREAKPOINT) { breakPoints_[bp].hasCond = false; + guard.unlock(); Update(addr); } } BreakPointCond *CBreakPoints::GetBreakPointCondition(u32 addr) { + std::lock_guard guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, true, false); if (bp != INVALID_BREAKPOINT && breakPoints_[bp].hasCond) return &breakPoints_[bp].cond; @@ -289,38 +329,44 @@ BreakPointCond *CBreakPoints::GetBreakPointCondition(u32 addr) } void CBreakPoints::ChangeBreakPointLogFormat(u32 addr, const std::string &fmt) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, true, false); if (bp != INVALID_BREAKPOINT) { breakPoints_[bp].logFormat = fmt; + guard.unlock(); Update(addr); } } BreakAction CBreakPoints::ExecBreakPoint(u32 addr) { + std::unique_lock guard(breakPointsMutex_); size_t bp = FindBreakpoint(addr, false); if (bp != INVALID_BREAKPOINT) { - if (breakPoints_[bp].hasCond) { + BreakPoint info = breakPoints_[bp]; + guard.unlock(); + + if (info.hasCond) { // Evaluate the breakpoint and abort if necessary. auto cond = CBreakPoints::GetBreakPointCondition(currentMIPS->pc); if (cond && !cond->Evaluate()) return BREAK_ACTION_IGNORE; } - if (breakPoints_[bp].result & BREAK_ACTION_LOG) { - if (breakPoints_[bp].logFormat.empty()) { + if (info.result & BREAK_ACTION_LOG) { + if (info.logFormat.empty()) { NOTICE_LOG(JIT, "BKP PC=%08x (%s)", addr, g_symbolMap->GetDescription(addr).c_str()); } else { std::string formatted; - CBreakPoints::EvaluateLogFormat(currentDebugMIPS, breakPoints_[bp].logFormat, formatted); + CBreakPoints::EvaluateLogFormat(currentDebugMIPS, info.logFormat, formatted); NOTICE_LOG(JIT, "BKP PC=%08x: %s", addr, formatted.c_str()); } } - if (breakPoints_[bp].result & BREAK_ACTION_PAUSE) { + if (info.result & BREAK_ACTION_PAUSE) { Core_EnableStepping(true); host->SetDebugMode(true); } - return breakPoints_[bp].result; + return info.result; } return BREAK_ACTION_IGNORE; @@ -328,6 +374,7 @@ BreakAction CBreakPoints::ExecBreakPoint(u32 addr) { void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, BreakAction result) { + std::unique_lock guard(memCheckMutex_); // This will ruin any pending memchecks. cleanupMemChecks_.clear(); @@ -341,18 +388,21 @@ void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, Break check.result = result; memChecks_.push_back(check); + guard.unlock(); Update(); } else { memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond); memChecks_[mc].result = (BreakAction)(memChecks_[mc].result | result); + guard.unlock(); Update(); } } void CBreakPoints::RemoveMemCheck(u32 start, u32 end) { + std::unique_lock guard(memCheckMutex_); // This will ruin any pending memchecks. cleanupMemChecks_.clear(); @@ -360,37 +410,44 @@ void CBreakPoints::RemoveMemCheck(u32 start, u32 end) if (mc != INVALID_MEMCHECK) { memChecks_.erase(memChecks_.begin() + mc); + guard.unlock(); Update(); } } void CBreakPoints::ChangeMemCheck(u32 start, u32 end, MemCheckCondition cond, BreakAction result) { + std::unique_lock guard(memCheckMutex_); size_t mc = FindMemCheck(start, end); if (mc != INVALID_MEMCHECK) { memChecks_[mc].cond = cond; memChecks_[mc].result = result; + guard.unlock(); Update(); } } void CBreakPoints::ClearAllMemChecks() { + std::unique_lock guard(memCheckMutex_); // This will ruin any pending memchecks. cleanupMemChecks_.clear(); if (!memChecks_.empty()) { memChecks_.clear(); + guard.unlock(); Update(); } } void CBreakPoints::ChangeMemCheckLogFormat(u32 start, u32 end, const std::string &fmt) { + std::unique_lock guard(memCheckMutex_); size_t mc = FindMemCheck(start, end); if (mc != INVALID_MEMCHECK) { memChecks_[mc].logFormat = fmt; + guard.unlock(); Update(); } } @@ -401,8 +458,12 @@ static inline u32 NotCached(u32 val) return val & ~0x40000000; } -MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) -{ +MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) { + std::lock_guard guard(memCheckMutex_); + return GetMemCheckLocked(address, size); +} + +MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) { std::vector::iterator iter; for (iter = memChecks_.begin(); iter != memChecks_.end(); ++iter) { @@ -425,9 +486,14 @@ MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc) { - auto check = GetMemCheck(address, size); - if (check) - return check->Action(address, write, size, pc); + std::unique_lock guard(memCheckMutex_); + auto check = GetMemCheckLocked(address, size); + if (check) { + check->Apply(address, write, size, pc); + auto copy = *check; + guard.unlock(); + return copy.Action(address, write, size, pc); + } return BREAK_ACTION_IGNORE; } @@ -443,15 +509,23 @@ BreakAction CBreakPoints::ExecOpMemCheck(u32 address, u32 pc) } bool write = MIPSAnalyst::IsOpMemoryWrite(pc); - auto check = GetMemCheck(address, size); + std::unique_lock guard(memCheckMutex_); + auto check = GetMemCheckLocked(address, size); if (check) { int mask = MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE; + bool apply = false; if (write && (check->cond & mask) == mask) { if (MIPSAnalyst::OpWouldChangeMemory(pc, address, size)) { - return check->Action(address, write, size, pc); + apply = true; } } else { - return check->Action(address, write, size, pc); + apply = true; + } + if (apply) { + check->Apply(address, write, size, pc); + auto copy = *check; + guard.unlock(); + return copy.Action(address, write, size, pc); } } return BREAK_ACTION_IGNORE; @@ -459,18 +533,28 @@ BreakAction CBreakPoints::ExecOpMemCheck(u32 address, u32 pc) void CBreakPoints::ExecMemCheckJitBefore(u32 address, bool write, int size, u32 pc) { - auto check = GetMemCheck(address, size); + std::unique_lock guard(memCheckMutex_); + auto check = GetMemCheckLocked(address, size); if (check) { - check->JitBefore(address, write, size, pc); + check->JitBeforeApply(address, write, size, pc); + auto copy = *check; + guard.unlock(); + copy.JitBeforeAction(address, write, size, pc); + guard.lock(); cleanupMemChecks_.push_back(check); } } void CBreakPoints::ExecMemCheckJitCleanup() { + std::unique_lock guard(memCheckMutex_); for (auto it = cleanupMemChecks_.begin(), end = cleanupMemChecks_.end(); it != end; ++it) { auto check = *it; - check->JitCleanup(); + bool changed = check->JitApplyChanged(); + auto copy = *check; + guard.unlock(); + copy.JitCleanup(changed); + guard.lock(); } cleanupMemChecks_.clear(); } @@ -489,6 +573,7 @@ u32 CBreakPoints::CheckSkipFirst() } const std::vector CBreakPoints::GetMemCheckRanges(bool write) { + std::lock_guard guard(memCheckMutex_); std::vector ranges = memChecks_; for (const auto &check : memChecks_) { if (!(check.cond & MEMCHECK_READ) && !write) @@ -509,16 +594,19 @@ const std::vector CBreakPoints::GetMemCheckRanges(bool write) { const std::vector CBreakPoints::GetMemChecks() { + std::lock_guard guard(memCheckMutex_); return memChecks_; } const std::vector CBreakPoints::GetBreakpoints() { + std::lock_guard guard(breakPointsMutex_); return breakPoints_; } bool CBreakPoints::HasMemChecks() { + std::lock_guard guard(memCheckMutex_); return !memChecks_.empty(); } diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 8cd7acdc4d..5c58042904 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -93,9 +93,14 @@ struct MemCheck { u32 lastAddr = 0; int lastSize = 0; + // Called on the stored memcheck (affects numHits, etc.) + BreakAction Apply(u32 addr, bool write, int size, u32 pc); + // Called on a copy. BreakAction Action(u32 addr, bool write, int size, u32 pc); - void JitBefore(u32 addr, bool write, int size, u32 pc); - void JitCleanup(); + void JitBeforeApply(u32 addr, bool write, int size, u32 pc); + void JitBeforeAction(u32 addr, bool write, int size, u32 pc); + bool JitApplyChanged(); + void JitCleanup(bool changed); void Log(u32 addr, bool write, int size, u32 pc); @@ -172,6 +177,7 @@ private: static size_t FindBreakpoint(u32 addr, bool matchTemp = false, bool temp = false); // Finds exactly, not using a range check. static size_t FindMemCheck(u32 start, u32 end); + static MemCheck *GetMemCheckLocked(u32 address, int size); static std::vector breakPoints_; static u32 breakSkipFirstAt_; From 22940f039325adcb5289916f827e7f5e10e68a3d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 2 May 2018 20:31:39 -0700 Subject: [PATCH 194/301] Debugger: Avoid asserts in disassembly. --- Core/MIPS/MIPSDisVFPU.cpp | 62 ++++++++++----------- Core/MIPS/MIPSVFPUUtils.cpp | 108 +++++++++++++++++++++++++----------- Core/MIPS/MIPSVFPUUtils.h | 6 ++ 3 files changed, 113 insertions(+), 63 deletions(-) diff --git a/Core/MIPS/MIPSDisVFPU.cpp b/Core/MIPS/MIPSDisVFPU.cpp index 3a0b16dfd7..772bf691a0 100644 --- a/Core/MIPS/MIPSDisVFPU.cpp +++ b/Core/MIPS/MIPSDisVFPU.cpp @@ -258,7 +258,7 @@ namespace MIPSDis { const char *name = MIPSGetName(op); int vd = _VD; - MatrixSize sz = GetMtxSize(op); + MatrixSize sz = GetMtxSizeSafe(op); sprintf(out, "%s%s\t%s",name,VSuff(op),MN(vd, sz)); } void Dis_MatrixSet2(MIPSOpcode op, char *out) @@ -266,7 +266,7 @@ namespace MIPSDis const char *name = MIPSGetName(op); int vd = _VD; int vs = _VS; - MatrixSize sz = GetMtxSize(op); + MatrixSize sz = GetMtxSizeSafe(op); sprintf(out, "%s%s\t%s, %s",name,VSuff(op),MN(vd, sz),MN(vs,sz)); } void Dis_MatrixSet3(MIPSOpcode op, char *out) @@ -275,7 +275,7 @@ namespace MIPSDis int vd = _VD; int vs = _VS; int vt = _VT; - MatrixSize sz = GetMtxSize(op); + MatrixSize sz = GetMtxSizeSafe(op); sprintf(out, "%s%s\t%s, %s, %s",name,VSuff(op),MN(vd, sz),MN(vs,sz),MN(vt,sz)); } @@ -285,7 +285,7 @@ namespace MIPSDis int vd = _VD; int vs = _VS; int vt = _VT; - MatrixSize sz = GetMtxSize(op); + MatrixSize sz = GetMtxSizeSafe(op); // TODO: Xpose? sprintf(out, "%s%s\t%s, %s, %s",name,VSuff(op),MN(vd, sz),MN(Xpose(vs),sz),MN(vt,sz)); } @@ -296,7 +296,7 @@ namespace MIPSDis int vd = _VD; int vs = _VS; int vt = _VT; - MatrixSize sz = GetMtxSize(op); + MatrixSize sz = GetMtxSizeSafe(op); sprintf(out, "%s%s\t%s, %s, %s", name, VSuff(op), MN(vd, sz), MN(vs, sz), VN(vt, V_Single)); } @@ -306,7 +306,7 @@ namespace MIPSDis int vd = _VD; int vs = _VS; int vt = _VT; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); sprintf(out, "%s%s\t%s, %s, %s", name, VSuff(op), VN(vd, V_Single), VN(vs,sz), VN(vt, sz)); } @@ -316,8 +316,8 @@ namespace MIPSDis int vs = _VS; int vt = _VT; int ins = (op>>23) & 7; - VectorSize sz = GetVecSize(op); - MatrixSize msz = GetMtxSize(op); + VectorSize sz = GetVecSizeSafe(op); + MatrixSize msz = GetMtxSizeSafe(op); int n = GetNumVectorElements(sz); if (n == ins) @@ -346,7 +346,7 @@ namespace MIPSDis int vt = _VT; int vs = _VS; int vd = _VS; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); if (sz != V_Triple) { sprintf(out, "vcrs\tERROR"); @@ -362,7 +362,7 @@ namespace MIPSDis int vt = _VT; int vs = _VS; int cond = op&15; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); const char *condNames[16] = {"FL","EQ","LT","LE","TR","NE","GE","GT","EZ","EN","EI","ES","NZ","NN","NI","NS"}; sprintf(out, "%s%s\t%s, %s, %s", name, VSuff(op), condNames[cond], VN(vs, sz), VN(vt,sz)); } @@ -370,7 +370,7 @@ namespace MIPSDis void Dis_Vcmov(MIPSOpcode op, char *out) { const char *name = MIPSGetName(op); - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int vd = _VD; int vs = _VS; int tf = (op >> 19)&3; @@ -391,7 +391,7 @@ namespace MIPSDis const char *name = MIPSGetName(op); int vd = _VD; int vs = _VS; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); sprintf(out, "%s%s\t%s, %s", name, VSuff(op), VN(vd, V_Single), VN(vs,sz)); } @@ -401,7 +401,7 @@ namespace MIPSDis int vd = _VD; int vs = _VS; int vt = _VT; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); sprintf(out, "%s%s\t%s, %s, %s", name, VSuff(op), VN(vd, sz), VN(vs,sz), VN(vt, V_Single)); } @@ -409,7 +409,7 @@ namespace MIPSDis { const char *name = MIPSGetName(op); int vd = _VD; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); sprintf(out, "%s%s\t%s",name,VSuff(op),VN(vd, sz)); } void Dis_VectorSet2(MIPSOpcode op, char *out) @@ -417,7 +417,7 @@ namespace MIPSDis const char *name = MIPSGetName(op); int vd = _VD; int vs = _VS; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); sprintf(out, "%s%s\t%s, %s",name,VSuff(op),VN(vd, sz),VN(vs, sz)); } void Dis_VectorSet3(MIPSOpcode op, char *out) @@ -426,7 +426,7 @@ namespace MIPSDis int vd = _VD; int vs = _VS; int vt = _VT; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); sprintf(out, "%s%s\t%s, %s, %s", name, VSuff(op), VN(vd, sz), VN(vs,sz), VN(vt, sz)); } @@ -445,7 +445,7 @@ namespace MIPSDis } c[(imm>>2) & 3] = 'S'; c[imm&3] = 'C'; - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int numElems = GetNumVectorElements(sz); int pos = 0; temp[pos++] = '['; @@ -465,7 +465,7 @@ namespace MIPSDis void Dis_CrossQuat(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); const char *name; switch (sz) { @@ -490,7 +490,7 @@ namespace MIPSDis void Dis_Vbfy(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int vd = _VD; int vs = _VS; const char *name = MIPSGetName(op); @@ -499,7 +499,7 @@ namespace MIPSDis void Dis_Vf2i(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int vd = _VD; int vs = _VS; int imm = (op>>16)&0x1f; @@ -509,7 +509,7 @@ namespace MIPSDis void Dis_Vs2i(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int vd = _VD; int vs = _VS; const char *name = MIPSGetName(op); @@ -518,8 +518,8 @@ namespace MIPSDis void Dis_Vi2x(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); - VectorSize dsz = GetHalfVectorSize(sz); + VectorSize sz = GetVecSizeSafe(op); + VectorSize dsz = GetHalfVectorSizeSafe(sz); if (((op>>16)&3)==0) dsz = V_Single; @@ -531,7 +531,7 @@ namespace MIPSDis void Dis_Vwbn(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int vd = _VD; int vs = _VS; @@ -542,8 +542,8 @@ namespace MIPSDis void Dis_Vf2h(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); - VectorSize dsz = GetHalfVectorSize(sz); + VectorSize sz = GetVecSizeSafe(op); + VectorSize dsz = GetHalfVectorSizeSafe(sz); if (((op>>16)&3)==0) dsz = V_Single; @@ -555,8 +555,8 @@ namespace MIPSDis void Dis_Vh2f(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); - VectorSize dsz = GetDoubleVectorSize(sz); + VectorSize sz = GetVecSizeSafe(op); + VectorSize dsz = GetDoubleVectorSizeSafe(sz); int vd = _VD; int vs = _VS; @@ -566,8 +566,8 @@ namespace MIPSDis void Dis_ColorConv(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); - VectorSize dsz = GetHalfVectorSize(sz); + VectorSize sz = GetVecSizeSafe(op); + VectorSize dsz = GetHalfVectorSizeSafe(sz); int vd = _VD; int vs = _VS; @@ -584,7 +584,7 @@ namespace MIPSDis void Dis_VrndX(MIPSOpcode op, char *out) { - VectorSize sz = GetVecSize(op); + VectorSize sz = GetVecSizeSafe(op); int vd = _VD; const char *name = MIPSGetName(op); diff --git a/Core/MIPS/MIPSVFPUUtils.cpp b/Core/MIPS/MIPSVFPUUtils.cpp index 5c1b35fffb..e493074665 100644 --- a/Core/MIPS/MIPSVFPUUtils.cpp +++ b/Core/MIPS/MIPSVFPUUtils.cpp @@ -303,89 +303,133 @@ int GetNumVectorElements(VectorSize sz) { } } -VectorSize GetHalfVectorSize(VectorSize sz) { +VectorSize GetHalfVectorSizeSafe(VectorSize sz) { switch (sz) { case V_Pair: return V_Single; case V_Quad: return V_Pair; - default: _assert_msg_(JIT, 0, "%s: Bad vector size", __FUNCTION__); return V_Invalid; + default: return V_Invalid; } } -VectorSize GetDoubleVectorSize(VectorSize sz) -{ - switch (sz) - { +VectorSize GetHalfVectorSize(VectorSize sz) { + VectorSize res = GetHalfVectorSizeSafe(sz); + _assert_msg_(JIT, res != V_Invalid, "%s: Bad vector size", __FUNCTION__); + return res; +} + +VectorSize GetDoubleVectorSizeSafe(VectorSize sz) { + switch (sz) { case V_Single: return V_Pair; case V_Pair: return V_Quad; - default: _assert_msg_(JIT, 0, "%s: Bad vector size", __FUNCTION__); return V_Invalid; + default: return V_Invalid; } } -VectorSize GetVecSize(MIPSOpcode op) -{ - int a = (op>>7)&1; - int b = (op>>15)&1; - a += (b<<1); - switch (a) - { - case 0: return V_Single; - case 1: return V_Pair; - case 2: return V_Triple; - case 3: return V_Quad; - default: _assert_msg_(JIT, 0, "%s: Bad vector size", __FUNCTION__); return V_Invalid; +VectorSize GetDoubleVectorSize(VectorSize sz) { + VectorSize res = GetDoubleVectorSizeSafe(sz); + _assert_msg_(JIT, res != V_Invalid, "%s: Bad vector size", __FUNCTION__); + return res; +} + +VectorSize GetVecSizeSafe(MIPSOpcode op) { + int a = (op >> 7) & 1; + int b = (op >> 15) & 1; + a += (b << 1); + switch (a) { + case 0: return V_Single; + case 1: return V_Pair; + case 2: return V_Triple; + case 3: return V_Quad; + default: return V_Invalid; } } -VectorSize GetVectorSize(MatrixSize sz) { +VectorSize GetVecSize(MIPSOpcode op) { + VectorSize res = GetVecSizeSafe(op); + _assert_msg_(JIT, res != V_Invalid, "%s: Bad vector size", __FUNCTION__); + return res; +} + +VectorSize GetVectorSizeSafe(MatrixSize sz) { switch (sz) { case M_2x2: return V_Pair; case M_3x3: return V_Triple; case M_4x4: return V_Quad; - default: _assert_msg_(JIT, 0, "%s: Bad vector size", __FUNCTION__); return V_Invalid; + default: return V_Invalid; } } -MatrixSize GetMatrixSize(VectorSize sz) { +VectorSize GetVectorSize(MatrixSize sz) { + VectorSize res = GetVectorSizeSafe(sz); + _assert_msg_(JIT, res != V_Invalid, "%s: Bad vector size", __FUNCTION__); + return res; +} + +MatrixSize GetMatrixSizeSafe(VectorSize sz) { switch (sz) { case V_Single: return M_Invalid; case V_Pair: return M_2x2; case V_Triple: return M_3x3; case V_Quad: return M_4x4; - default: _assert_msg_(JIT, 0, "%s: Bad vector size", __FUNCTION__); return M_Invalid; + default: return M_Invalid; } } -MatrixSize GetMtxSize(MIPSOpcode op) { - int a = (op>>7)&1; - int b = (op>>15)&1; - a += (b<<1); +MatrixSize GetMatrixSize(VectorSize sz) { + MatrixSize res = GetMatrixSizeSafe(sz); + _assert_msg_(JIT, res != M_Invalid, "%s: Bad vector size", __FUNCTION__); + return res; +} + +MatrixSize GetMtxSizeSafe(MIPSOpcode op) { + int a = (op >> 7) & 1; + int b = (op >> 15) & 1; + a += (b << 1); switch (a) { case 0: return M_4x4; // This happens in disassembly of junk case 1: return M_2x2; case 2: return M_3x3; case 3: return M_4x4; - default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); return M_Invalid; + default: return M_Invalid; } } -VectorSize MatrixVectorSize(MatrixSize sz) { +MatrixSize GetMtxSize(MIPSOpcode op) { + MatrixSize res = GetMtxSizeSafe(op); + _assert_msg_(JIT, res != M_Invalid, "%s: Bad matrix size", __FUNCTION__); + return res; +} + +VectorSize MatrixVectorSizeSafe(MatrixSize sz) { switch (sz) { case M_2x2: return V_Pair; case M_3x3: return V_Triple; case M_4x4: return V_Quad; - default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); return V_Invalid; + default: return V_Invalid; } } -int GetMatrixSide(MatrixSize sz) { +VectorSize MatrixVectorSize(MatrixSize sz) { + VectorSize res = MatrixVectorSizeSafe(sz); + _assert_msg_(JIT, res != V_Invalid, "%s: Bad matrix size", __FUNCTION__); + return res; +} + +int GetMatrixSideSafe(MatrixSize sz) { switch (sz) { case M_2x2: return 2; case M_3x3: return 3; case M_4x4: return 4; - default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__); return 0; + default: return 0; } } +int GetMatrixSide(MatrixSize sz) { + int res = MatrixVectorSizeSafe(sz); + _assert_msg_(JIT, res != 0, "%s: Bad matrix size", __FUNCTION__); + return res; +} + // TODO: Optimize MatrixOverlapType GetMatrixOverlap(int mtx1, int mtx2, MatrixSize msize) { int n = GetMatrixSide(msize); diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index 2e2cb62040..8e25821b1d 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -158,12 +158,18 @@ inline int GetMtx(int matrixReg) { return (matrixReg >> 2) & 7; } +VectorSize GetVecSizeSafe(MIPSOpcode op); VectorSize GetVecSize(MIPSOpcode op); +MatrixSize GetMtxSizeSafe(MIPSOpcode op); MatrixSize GetMtxSize(MIPSOpcode op); +VectorSize GetHalfVectorSizeSafe(VectorSize sz); VectorSize GetHalfVectorSize(VectorSize sz); +VectorSize GetDoubleVectorSizeSafe(VectorSize sz); VectorSize GetDoubleVectorSize(VectorSize sz); +VectorSize MatrixVectorSizeSafe(MatrixSize sz); VectorSize MatrixVectorSize(MatrixSize sz); int GetNumVectorElements(VectorSize sz); +int GetMatrixSideSafe(MatrixSize sz); int GetMatrixSide(MatrixSize sz); const char *GetVectorNotation(int reg, VectorSize size); const char *GetMatrixNotation(int reg, MatrixSize size); From 18dcea4cdc9fe1402f98cc47a3a85b606d3afbe6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 3 May 2018 06:19:48 -0700 Subject: [PATCH 195/301] Debugger: Disasm search API. --- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 89 +++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 133f845bcd..6f928c4a7e 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "base/stringutil.h" #include "util/text/utf8.h" #include "Core/Debugger/Breakpoints.h" @@ -33,11 +34,13 @@ struct WebSocketDisasmState { void Base(DebuggerRequest &req); void Disasm(DebuggerRequest &req); + void SearchDisasm(DebuggerRequest &req); void Assemble(DebuggerRequest &req); protected: void WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l); void WriteBranchGuide(JsonWriter &json, const BranchLine &l); + uint32_t RoundAddressUp(uint32_t addr); DisassemblyManager disasm_; }; @@ -46,6 +49,7 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) { auto p = new WebSocketDisasmState(); map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1); map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1); + map["memory.searchDisasm"] = std::bind(&WebSocketDisasmState::SearchDisasm, p, std::placeholders::_1); map["memory.assemble"] = std::bind(&WebSocketDisasmState::Assemble, p, std::placeholders::_1); return p; @@ -97,9 +101,9 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi json.writeBool("enabled", enabled); auto cond = CBreakPoints::GetBreakPointCondition(addr); if (cond) - json.writeString("expression", cond->expressionString); + json.writeString("condition", cond->expressionString); else - json.writeNull("expression"); + json.writeNull("condition"); json.pop(); } else { json.writeNull("breakpoint"); @@ -194,6 +198,18 @@ void WebSocketDisasmState::WriteBranchGuide(JsonWriter &json, const BranchLine & json.pop(); } +uint32_t WebSocketDisasmState::RoundAddressUp(uint32_t addr) { + if (addr < PSP_GetScratchpadMemoryBase()) + return PSP_GetScratchpadMemoryBase(); + else if (addr >= PSP_GetScratchpadMemoryEnd() && addr < PSP_GetVidMemBase()) + return PSP_GetVidMemBase(); + else if (addr >= PSP_GetVidMemEnd() && addr < PSP_GetKernelMemoryBase()) + return PSP_GetKernelMemoryBase(); + else if (addr >= PSP_GetUserMemoryEnd()) + return PSP_GetScratchpadMemoryBase(); + return addr; +} + // Request the current PSP memory base address (memory.base) // // WARNING: Avoid this unless you have a good reason. Uses PPSSPP's address space. @@ -308,6 +324,75 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { json.pop(); } +void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { + return req.Fail("CPU not started"); + } + + uint32_t start; + if (!req.ParamU32("address", &start)) + return; + uint32_t end = start; + if (!req.ParamU32("end", &end, false, DebuggerParamType::OPTIONAL)) + return; + std::string match; + if (!req.ParamString("match", &match)) + return; + bool displaySymbols = true; + if (!req.ParamBool("displaySymbols", &displaySymbols, DebuggerParamType::OPTIONAL)) + return; + + bool loopSearch = end <= start; + start = RoundAddressUp(start); + if ((end <= start) != loopSearch) { + // We must've passed end by rounding up. + JsonWriter &json = req.Respond(); + json.writeNull("address"); + return; + } + + // We do this after the check in case both were in unused memory. + end = RoundAddressUp(end); + + std::transform(match.begin(), match.end(), match.begin(), ::tolower); + + DisassemblyLineInfo line; + bool found = false; + uint32_t addr = start; + do { + disasm_.getLine(addr, displaySymbols, line); + const std::string addressSymbol = g_symbolMap->GetLabelString(addr); + + std::string mergeForSearch; + // Address+space (9) + symbol + colon+space (2) + name + space(1) + params = 12 fixed size worst case. + mergeForSearch.resize(12 + addressSymbol.size() + line.name.size() + line.params.size()); + + sprintf(&mergeForSearch[0], "%08x ", addr); + auto inserter = mergeForSearch.begin() + 9; + if (!addressSymbol.empty()) { + inserter = std::transform(addressSymbol.begin(), addressSymbol.end(), inserter, ::tolower); + *inserter++ = ':'; + *inserter++ = ' '; + } + inserter = std::transform(line.name.begin(), line.name.end(), inserter, ::tolower); + *inserter++ = ' '; + inserter = std::transform(line.params.begin(), line.params.end(), inserter, ::tolower); + + if (mergeForSearch.find(match) != mergeForSearch.npos) { + found = true; + break; + } + + addr = RoundAddressUp(addr + line.totalSize); + } while (addr != end); + + JsonWriter &json = req.Respond(); + if (found) + json.writeUint("address", addr); + else + json.writeNull("address"); +} + void WebSocketDisasmState::Assemble(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); From 29d93c56c7ddf417d02065f20e3ce6d344c46feb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 3 May 2018 06:20:20 -0700 Subject: [PATCH 196/301] Debugger: Initial breakpoint APIs. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Debugger/WebSocket.cpp | 2 + .../WebSocket/BreakpointSubscriber.cpp | 179 ++++++++++++++++++ .../Debugger/WebSocket/BreakpointSubscriber.h | 27 +++ Core/Debugger/WebSocket/WebSocketUtils.cpp | 13 +- Core/Debugger/WebSocket/WebSocketUtils.h | 1 + android/jni/Android.mk | 1 + 9 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 Core/Debugger/WebSocket/BreakpointSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/BreakpointSubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 608d057425..a1cc6b95f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1404,6 +1404,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/DisassemblyManager.h Core/Debugger/WebSocket.cpp Core/Debugger/WebSocket.h + Core/Debugger/WebSocket/BreakpointSubscriber.cpp + Core/Debugger/WebSocket/BreakpointSubscriber.h Core/Debugger/WebSocket/CPUCoreSubscriber.cpp Core/Debugger/WebSocket/CPUCoreSubscriber.h Core/Debugger/WebSocket/DisasmSubscriber.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c06e5f1e78..a9d703869f 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -185,6 +185,7 @@ + @@ -542,6 +543,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 796be8ac6d..c5fe328767 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -722,6 +722,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1331,6 +1334,9 @@ Debugger\WebSocket + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index dc7d44c59b..3ae93962a8 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -47,6 +47,7 @@ #include "Core/Debugger/WebSocket/LogBroadcaster.h" #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" +#include "Core/Debugger/WebSocket/BreakpointSubscriber.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h" @@ -60,6 +61,7 @@ struct SubscriberInfo { }; static const std::vector subscribers({ + { &WebSocketBreakpointInit, nullptr }, { &WebSocketCPUCoreInit, nullptr }, { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, { &WebSocketGameInit, nullptr }, diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp new file mode 100644 index 0000000000..d93bd34051 --- /dev/null +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Common/StringUtils.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/WebSocket/BreakpointSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MIPS/MIPSDebugInterface.h" + +void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) { + // No need to bind or alloc state, these are all global. + map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd; + map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate; + map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove; + map["cpu.breakpoint.list"] = &WebSocketCPUBreakpointList; + + return nullptr; +} + +struct WebSocketCPUBreakpointParams { + uint32_t address = 0; + bool hasEnabled = false; + bool hasLog = false; + bool hasCondition = false; + bool hasLogFormat = false; + + bool enabled; + bool log; + std::string condition; + PostfixExpression compiledCondition; + std::string logFormat; + + bool Parse(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + req.Fail("CPU not started"); + return false; + } + + if (!req.ParamU32("address", &address)) + return false; + + hasEnabled = req.HasParam("enabled"); + if (hasEnabled) { + if (!req.ParamBool("enabled", &enabled)) + return false; + } + hasLog = req.HasParam("log"); + if (hasLog) { + if (!req.ParamBool("log", &log)) + return false; + } + hasCondition = req.HasParam("condition"); + if (hasCondition) { + if (!req.ParamString("condition", &condition)) + return false; + if (!currentDebugMIPS->initExpression(condition.c_str(), compiledCondition)) { + req.Fail(StringFromFormat("Could not parse expression syntax: %s", getExpressionError())); + return false; + } + } + hasLogFormat = req.HasParam("logFormat"); + if (hasLogFormat) { + if (!req.ParamString("logFormat", &logFormat)) + return false; + } + + return true; + } + + void Apply() { + if (hasCondition && !condition.empty()) { + BreakPointCond cond; + cond.debug = currentDebugMIPS; + cond.expressionString = condition; + cond.expression = compiledCondition; + CBreakPoints::ChangeBreakPointAddCond(address, cond); + } else if (hasCondition && condition.empty()) { + CBreakPoints::ChangeBreakPointRemoveCond(address); + } + + if (hasLogFormat) { + CBreakPoints::ChangeBreakPointLogFormat(address, logFormat); + } + + // TODO: Fix this interface. + if (hasLog && !hasEnabled) { + CBreakPoints::IsAddressBreakPoint(address, &enabled); + hasEnabled = true; + } + if (hasLog && hasEnabled) { + BreakAction result = BREAK_ACTION_IGNORE; + if (log) + result |= BREAK_ACTION_LOG; + if (enabled) + result |= BREAK_ACTION_PAUSE; + CBreakPoints::ChangeBreakPoint(address, result); + } else if (hasEnabled) { + CBreakPoints::ChangeBreakPoint(address, enabled); + } + } +}; + +void WebSocketCPUBreakpointAdd(DebuggerRequest &req) { + WebSocketCPUBreakpointParams params; + if (!params.Parse(req)) + return; + + CBreakPoints::AddBreakPoint(params.address); + params.Apply(); + req.Respond(); +} + +void WebSocketCPUBreakpointUpdate(DebuggerRequest &req) { + WebSocketCPUBreakpointParams params; + if (!params.Parse(req)) + return; + bool enabled; + if (!CBreakPoints::IsAddressBreakPoint(params.address, &enabled)) + return req.Fail("Breakpoint not found"); + + params.Apply(); + req.Respond(); +} + +void WebSocketCPUBreakpointRemove(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + uint32_t address; + if (!req.ParamU32("address", &address)) + return; + + CBreakPoints::RemoveBreakPoint(address); + req.Respond(); +} + +void WebSocketCPUBreakpointList(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + JsonWriter &json = req.Respond(); + json.pushArray("breakpoints"); + auto bps = CBreakPoints::GetBreakpoints(); + for (const auto &bp : bps) { + if (bp.temporary) + continue; + + json.pushDict(); + json.writeUint("address", bp.addr); + json.writeBool("enabled", bp.IsEnabled()); + json.writeBool("log", (bp.result & BREAK_ACTION_LOG) != 0); + if (bp.hasCond) + json.writeString("condition", bp.cond.expressionString); + else + json.writeNull("condition"); + if (!bp.logFormat.empty()) + json.writeString("logFormat", bp.logFormat); + else + json.writeNull("logFormat"); + json.pop(); + } + json.pop(); +} diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h new file mode 100644 index 0000000000..eccf23c3cb --- /dev/null +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h @@ -0,0 +1,27 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map); + +void WebSocketCPUBreakpointAdd(DebuggerRequest &req); +void WebSocketCPUBreakpointUpdate(DebuggerRequest &req); +void WebSocketCPUBreakpointRemove(DebuggerRequest &req); +void WebSocketCPUBreakpointList(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/WebSocketUtils.cpp b/Core/Debugger/WebSocket/WebSocketUtils.cpp index 54761aaa55..3a3536684c 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.cpp +++ b/Core/Debugger/WebSocket/WebSocketUtils.cpp @@ -85,6 +85,17 @@ static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) { return false; } +bool DebuggerRequest::HasParam(const char *name, bool ignoreNull) { + const JsonNode *node = data.get(name); + if (!node) { + return false; + } + if (node->value.getTag() == JSON_NULL) { + return !ignoreNull; + } + return true; +} + bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatBits, DebuggerParamType type) { bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE; bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE; @@ -136,7 +147,7 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatB return false; } if (tag != JSON_STRING) { - if (required || tag != JSON_NULL) { + if (type == DebuggerParamType::REQUIRED || tag != JSON_NULL) { Fail(StringFromFormat("Invalid '%s' parameter type", name)); return false; } diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index 807d8e3f10..e49647dcf8 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -80,6 +80,7 @@ struct DebuggerRequest { responseSent_ = true; } + bool HasParam(const char *name, bool ignoreNull = false); bool ParamU32(const char *name, uint32_t *out, bool allowFloatBits = false, DebuggerParamType type = DebuggerParamType::REQUIRED); bool ParamBool(const char *name, bool *out, DebuggerParamType type = DebuggerParamType::REQUIRED); bool ParamString(const char *name, std::string *out, DebuggerParamType type = DebuggerParamType::REQUIRED); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 35ee9c930e..b08ade3a28 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -302,6 +302,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/DisassemblyManager.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Debugger/WebSocket.cpp \ + $(SRC)/Core/Debugger/WebSocket/BreakpointSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/DisasmSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ From b2cc4a0965eea4836587b5642fca1e0d7fb99413 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 4 May 2018 23:04:17 -0700 Subject: [PATCH 197/301] Debugger: Add memory breakpoint management. --- Core/Debugger/Breakpoints.cpp | 17 +- Core/Debugger/Breakpoints.h | 3 +- Core/Debugger/DisassemblyManager.cpp | 1 - .../WebSocket/BreakpointSubscriber.cpp | 177 +++++++++++++++++- .../Debugger/WebSocket/BreakpointSubscriber.h | 5 + Core/Debugger/WebSocket/DisasmSubscriber.cpp | 21 +++ .../Debugger/WebSocket/SteppingSubscriber.cpp | 3 + Core/MIPS/x86/JitSafeMem.cpp | 12 +- Windows/Debugger/CtrlDisAsmView.cpp | 1 + 9 files changed, 227 insertions(+), 13 deletions(-) diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index c5515f4f34..d1b293c578 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -452,15 +452,28 @@ void CBreakPoints::ChangeMemCheckLogFormat(u32 start, u32 end, const std::string } } +bool CBreakPoints::GetMemCheck(u32 start, u32 end, MemCheck *check) { + std::lock_guard guard(memCheckMutex_); + size_t mc = FindMemCheck(start, end); + if (mc != INVALID_MEMCHECK) { + *check = memChecks_[mc]; + return true; + } + return false; +} + static inline u32 NotCached(u32 val) { // Remove the cached part of the address. return val & ~0x40000000; } -MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) { +bool CBreakPoints::GetMemCheckInRange(u32 address, int size, MemCheck *check) { std::lock_guard guard(memCheckMutex_); - return GetMemCheckLocked(address, size); + auto result = GetMemCheckLocked(address, size); + if (result) + *check = *result; + return result != nullptr; } MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) { diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 5c58042904..45f91d3cc8 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -149,7 +149,8 @@ public: static void ChangeMemCheckLogFormat(u32 start, u32 end, const std::string &fmt); - static MemCheck *GetMemCheck(u32 address, int size); + static bool GetMemCheck(u32 start, u32 end, MemCheck *check); + static bool GetMemCheckInRange(u32 address, int size, MemCheck *check); static BreakAction ExecMemCheck(u32 address, bool write, int size, u32 pc); static BreakAction ExecOpMemCheck(u32 address, u32 pc); diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 660ce2aa89..c5ab4b2e91 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -346,7 +346,6 @@ u32 DisassemblyManager::getNthNextAddress(u32 address, int n) } DisassemblyManager::~DisassemblyManager() { - clear(); } void DisassemblyManager::clear() diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index d93bd34051..aa6681cb82 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -17,6 +17,8 @@ #include "Common/StringUtils.h" #include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/WebSocket/BreakpointSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/MIPS/MIPSDebugInterface.h" @@ -28,6 +30,11 @@ void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) { map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove; map["cpu.breakpoint.list"] = &WebSocketCPUBreakpointList; + map["memory.breakpoint.add"] = &WebSocketMemoryBreakpointAdd; + map["memory.breakpoint.update"] = &WebSocketMemoryBreakpointUpdate; + map["memory.breakpoint.remove"] = &WebSocketMemoryBreakpointRemove; + map["memory.breakpoint.list"] = &WebSocketMemoryBreakpointList; + return nullptr; } @@ -57,12 +64,12 @@ struct WebSocketCPUBreakpointParams { if (hasEnabled) { if (!req.ParamBool("enabled", &enabled)) return false; - } + } hasLog = req.HasParam("log"); if (hasLog) { if (!req.ParamBool("log", &log)) return false; - } + } hasCondition = req.HasParam("condition"); if (hasCondition) { if (!req.ParamString("condition", &condition)) @@ -173,6 +180,172 @@ void WebSocketCPUBreakpointList(DebuggerRequest &req) { json.writeString("logFormat", bp.logFormat); else json.writeNull("logFormat"); + std::string symbol = g_symbolMap->GetLabelString(bp.addr); + if (symbol.empty()) + json.writeNull("symbol"); + else + json.writeString("symbol", symbol); + + DisassemblyManager manager; + DisassemblyLineInfo line; + manager.getLine(manager.getStartAddress(bp.addr), true, line); + json.writeString("code", line.name + " " + line.params); + + json.pop(); + } + json.pop(); +} + +struct WebSocketMemoryBreakpointParams { + uint32_t address = 0; + uint32_t end = 0; + bool hasEnabled = false; + bool hasLog = false; + bool hasCond = false; + bool hasLogFormat = false; + + bool enabled = true; + bool log = true; + MemCheckCondition cond = MEMCHECK_READWRITE; + std::string logFormat; + + bool Parse(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + req.Fail("CPU not started"); + return false; + } + + if (!req.ParamU32("address", &address)) + return false; + uint32_t size; + if (!req.ParamU32("size", &size)) + return false; + if (address + size < address) { + req.Fail("Size is too large"); + return false; + } + end = size == 0 ? 0 : address + size; + + hasEnabled = req.HasParam("enabled"); + if (hasEnabled) { + if (!req.ParamBool("enabled", &enabled)) + return false; + } + hasLog = req.HasParam("log"); + if (hasLog) { + if (!req.ParamBool("log", &log)) + return false; + } + hasCond = req.HasParam("read") || req.HasParam("write") || req.HasParam("change"); + if (hasCond) { + bool read, write, change; + if (!req.ParamBool("read", &read) || !req.ParamBool("write", &write) || !req.ParamBool("change", &change)) + return false; + int bits = (read ? MEMCHECK_READ : 0) | (write ? MEMCHECK_WRITE : 0) | (change ? MEMCHECK_WRITE_ONCHANGE : 0); + cond = MemCheckCondition(bits); + } + hasLogFormat = req.HasParam("logFormat"); + if (hasLogFormat) { + if (!req.ParamString("logFormat", &logFormat)) + return false; + } + + return true; + } + + BreakAction Result(bool adding) { + int bits = MEMCHECK_READWRITE; + if (adding || (hasLog && hasEnabled)) { + bits = (enabled ? BREAK_ACTION_PAUSE : 0) | (log ? BREAK_ACTION_LOG : 0); + } else { + MemCheck prev; + if (CBreakPoints::GetMemCheck(address, end, &prev)) + bits = prev.result; + + if (hasEnabled) + bits = (bits & ~BREAK_ACTION_PAUSE) | (enabled ? BREAK_ACTION_PAUSE : 0); + if (hasLog) + bits = (bits & ~BREAK_ACTION_LOG) | (log ? BREAK_ACTION_LOG : 0); + } + + return BreakAction(bits); + } + + void Apply() { + if (hasLogFormat) { + CBreakPoints::ChangeMemCheckLogFormat(address, end, logFormat); + } + } +}; + +void WebSocketMemoryBreakpointAdd(DebuggerRequest &req) { + WebSocketMemoryBreakpointParams params; + if (!params.Parse(req)) + return; + + CBreakPoints::AddMemCheck(params.address, params.end, params.cond, params.Result(true)); + params.Apply(); + req.Respond(); +} + +void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req) { + WebSocketMemoryBreakpointParams params; + if (!params.Parse(req)) + return; + + MemCheck mc; + if (!CBreakPoints::GetMemCheck(params.address, params.end, &mc)) + return req.Fail("Breakpoint not found"); + + CBreakPoints::ChangeMemCheck(params.address, params.end, params.cond, params.Result(true)); + params.Apply(); + req.Respond(); +} + +void WebSocketMemoryBreakpointRemove(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + uint32_t address; + if (!req.ParamU32("address", &address)) + return; + uint32_t size; + if (!req.ParamU32("size", &size)) + return; + + CBreakPoints::RemoveMemCheck(address, size == 0 ? 0 : address + size); + req.Respond(); +} + +void WebSocketMemoryBreakpointList(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + JsonWriter &json = req.Respond(); + json.pushArray("breakpoints"); + auto mcs = CBreakPoints::GetMemChecks(); + for (const auto &mc : mcs) { + json.pushDict(); + json.writeUint("address", mc.start); + json.writeUint("size", mc.end == 0 ? 0 : mc.end - mc.start); + json.writeBool("enabled", mc.IsEnabled()); + json.writeBool("log", (mc.result & BREAK_ACTION_LOG) != 0); + json.writeBool("read", (mc.cond & MEMCHECK_READ) != 0); + json.writeBool("write", (mc.cond & MEMCHECK_WRITE) != 0); + json.writeBool("change", (mc.cond & MEMCHECK_WRITE_ONCHANGE) != 0); + json.writeUint("hits", mc.numHits); + if (!mc.logFormat.empty()) + json.writeString("logFormat", mc.logFormat); + else + json.writeNull("logFormat"); + std::string symbol = g_symbolMap->GetLabelString(mc.start); + if (symbol.empty()) + json.writeNull("symbol"); + else + json.writeString("symbol", symbol); + json.pop(); } json.pop(); diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h index eccf23c3cb..c9c2e1effc 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.h +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h @@ -25,3 +25,8 @@ void WebSocketCPUBreakpointAdd(DebuggerRequest &req); void WebSocketCPUBreakpointUpdate(DebuggerRequest &req); void WebSocketCPUBreakpointRemove(DebuggerRequest &req); void WebSocketCPUBreakpointList(DebuggerRequest &req); + +void WebSocketMemoryBreakpointAdd(DebuggerRequest &req); +void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req); +void WebSocketMemoryBreakpointRemove(DebuggerRequest &req); +void WebSocketMemoryBreakpointList(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 6f928c4a7e..95dddcebb8 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -31,6 +31,9 @@ struct WebSocketDisasmState { WebSocketDisasmState() { disasm_.setCpu(currentDebugMIPS); } + ~WebSocketDisasmState() { + disasm_.clear(); + } void Base(DebuggerRequest &req); void Disasm(DebuggerRequest &req); @@ -324,6 +327,16 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { json.pop(); } +// Search disassembly for some text (cpu.searchDisasm) +// +// Parameters: +// - address: starting address as a number. +// - end: optional end address as a number (otherwise uses start.) +// - match: string to search for. +// - displaySymbols: optional, specify false to hide symbols in the searched parameters. +// +// Response (same event name): +// - address: number address of match or null if none was found. void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); @@ -393,6 +406,14 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { json.writeNull("address"); } +// Assemble an instruction (cpu.assemble) +// +// Parameters: +// - address: number indicating the address to write to. +// - code: string containing the instruction to assemble. +// +// Response (same event name): +// - encoding: resulting encoding at this address. Always returns one value, even for macros. void WebSocketDisasmState::Assemble(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index 3b3842353a..062064d9cd 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -32,6 +32,9 @@ struct WebSocketSteppingState { WebSocketSteppingState() { disasm_.setCpu(currentDebugMIPS); } + ~WebSocketSteppingState() { + disasm_.clear(); + } void Into(DebuggerRequest &req); void Over(DebuggerRequest &req); diff --git a/Core/MIPS/x86/JitSafeMem.cpp b/Core/MIPS/x86/JitSafeMem.cpp index 104b6579a0..676f8b1ced 100644 --- a/Core/MIPS/x86/JitSafeMem.cpp +++ b/Core/MIPS/x86/JitSafeMem.cpp @@ -346,14 +346,12 @@ void JitSafeMem::Finish() jit_->SetJumpTarget(*it); } -void JitSafeMem::MemCheckImm(MemoryOpType type) -{ - MemCheck *check = CBreakPoints::GetMemCheck(iaddr_, size_); - if (check) - { - if (!(check->cond & MEMCHECK_READ) && type == MEM_READ) +void JitSafeMem::MemCheckImm(MemoryOpType type) { + MemCheck check; + if (CBreakPoints::GetMemCheckInRange(iaddr_, size_, &check)) { + if (!(check.cond & MEMCHECK_READ) && type == MEM_READ) return; - if (!(check->cond & MEMCHECK_WRITE) && type == MEM_WRITE) + if (!(check.cond & MEMCHECK_WRITE) && type == MEM_WRITE) return; jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC())); diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index fecc7de397..7b8ea04406 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -183,6 +183,7 @@ CtrlDisAsmView::~CtrlDisAsmView() { DeleteObject(font); DeleteObject(boldfont); + manager.clear(); } COLORREF scaleColor(COLORREF color, float factor) From 8598fc99129c0bebcdf0b899108111cd989a18ae Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 May 2018 10:35:56 -0700 Subject: [PATCH 198/301] Debugger: Add some missing memory locks. We can restart memory when loading save states, so we need this even while we've got startup/shutdown locked. --- Core/Debugger/DisassemblyManager.cpp | 2 ++ Core/Debugger/WebSocket/DisasmSubscriber.cpp | 3 +++ Windows/Debugger/CtrlDisAsmView.cpp | 4 +++- Windows/Debugger/CtrlMemView.cpp | 5 +++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index c5ab4b2e91..54ffae28d4 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -238,6 +238,8 @@ std::vector DisassemblyManager::getBranchLines(u32 start, u32 size) void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest) { + // This is here really to avoid lock ordering issues. + auto memLock = Memory::Lock(); std::lock_guard guard(entriesLock_); auto it = findDisassemblyEntry(entries,address,false); if (it == entries.end()) diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 95dddcebb8..84d0692ae2 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -256,6 +256,7 @@ void WebSocketDisasmState::Base(DebuggerRequest &req) { // - params: formatted parameters for the instruction. // - (other info about the disassembled line.) void WebSocketDisasmState::Disasm(DebuggerRequest &req) { + auto memLock = Memory::Lock(); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } @@ -338,6 +339,7 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { // Response (same event name): // - address: number address of match or null if none was found. void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { + auto memLock = Memory::Lock(); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } @@ -415,6 +417,7 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { // Response (same event name): // - encoding: resulting encoding at this address. Always returns one value, even for macros. void WebSocketDisasmState::Assemble(DebuggerRequest &req) { + auto memLock = Memory::Lock(); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 7b8ea04406..93d4ed6178 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -201,7 +201,6 @@ COLORREF scaleColor(COLORREF color, float factor) bool CtrlDisAsmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) { - auto memLock = Memory::Lock(); if (!PSP_IsInited()) return false; @@ -469,6 +468,7 @@ void CtrlDisAsmView::drawArguments(HDC hdc, const DisassemblyLineInfo &line, int void CtrlDisAsmView::onPaint(WPARAM wParam, LPARAM lParam) { + auto memLock = Memory::Lock(); if (!debugger->isAlive()) return; PAINTSTRUCT ps; @@ -1180,6 +1180,7 @@ void CtrlDisAsmView::calculatePixelPositions() void CtrlDisAsmView::search(bool continueSearch) { + auto memLock = Memory::Lock(); u32 searchAddress; if (continueSearch == false || searchQuery[0] == 0) @@ -1260,6 +1261,7 @@ void CtrlDisAsmView::search(bool continueSearch) std::string CtrlDisAsmView::disassembleRange(u32 start, u32 size) { + auto memLock = Memory::Lock(); std::string result; // gather all branch targets without labels diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index 4cede1270e..f1cc93e372 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -174,6 +174,8 @@ CtrlMemView *CtrlMemView::getFrom(HWND hwnd) void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) { + auto memLock = Memory::Lock(); + // draw to a bitmap for double buffering PAINTSTRUCT ps; HDC actualHdc = BeginPaint(wnd, &ps); @@ -445,6 +447,7 @@ void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) case ID_MEMVIEW_COPYVALUE_8: { + auto memLock = Memory::Lock(); char temp[24]; // it's admittedly not really useful like this @@ -462,6 +465,7 @@ void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) case ID_MEMVIEW_COPYVALUE_16: { + auto memLock = Memory::Lock(); char temp[24]; sprintf(temp,"%04X",Memory::IsValidAddress(curAddress) ? Memory::Read_U16(curAddress) : 0xFFFF); @@ -471,6 +475,7 @@ void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) case ID_MEMVIEW_COPYVALUE_32: { + auto memLock = Memory::Lock(); char temp[24]; sprintf(temp,"%08X",Memory::IsValidAddress(curAddress) ? Memory::Read_U32(curAddress) : 0xFFFFFFFF); From 3193772e78d7c58ab7fecb07c5a860337314bb0a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 May 2018 10:40:02 -0700 Subject: [PATCH 199/301] Debugger: Use a lock for memory reallocs. Simpler this way, no need to remember to lock memory. --- Core/Core.h | 4 ++++ Core/Debugger/WebSocket.cpp | 3 +++ Core/Debugger/WebSocket/DisasmSubscriber.cpp | 3 --- Core/MemMap.cpp | 14 ++++++++++---- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Core/Core.h b/Core/Core.h index 84fe707792..0b87f2b393 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -45,6 +45,10 @@ enum class CoreLifecycle { STOPPING, // Guaranteed call after STOPPING. STOPPED, + + // Sometimes called for save states. Guaranteed sequence, and never during STARTING or STOPPING. + MEMORY_REINITING, + MEMORY_REINITED, }; typedef void (* CoreLifecycleFunc)(CoreLifecycle stage); diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 3ae93962a8..f477746c78 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -20,6 +20,7 @@ #include "thread/threadutil.h" #include "Core/Debugger/WebSocket.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MemMap.h" // This WebSocket (connected through the same port as disc sharing) allows API/debugger access to PPSSPP. // Currently, the only subprotocol "debugger.ppsspp.org" uses a simple JSON based interface. @@ -88,6 +89,7 @@ static void WebSocketNotifyLifecycle(CoreLifecycle stage) { switch (stage) { case CoreLifecycle::STARTING: case CoreLifecycle::STOPPING: + case CoreLifecycle::MEMORY_REINITING: if (debuggersConnected > 0) { DEBUG_LOG(SYSTEM, "Waiting for debugger to complete on shutdown"); } @@ -96,6 +98,7 @@ static void WebSocketNotifyLifecycle(CoreLifecycle stage) { case CoreLifecycle::START_COMPLETE: case CoreLifecycle::STOPPED: + case CoreLifecycle::MEMORY_REINITED: lifecycleLock.unlock(); if (debuggersConnected > 0) { DEBUG_LOG(SYSTEM, "Debugger ready for shutdown"); diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 84d0692ae2..95dddcebb8 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -256,7 +256,6 @@ void WebSocketDisasmState::Base(DebuggerRequest &req) { // - params: formatted parameters for the instruction. // - (other info about the disassembled line.) void WebSocketDisasmState::Disasm(DebuggerRequest &req) { - auto memLock = Memory::Lock(); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } @@ -339,7 +338,6 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { // Response (same event name): // - address: number address of match or null if none was found. void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { - auto memLock = Memory::Lock(); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } @@ -417,7 +415,6 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { // Response (same event name): // - encoding: resulting encoding at this address. Always returns one value, even for macros. void WebSocketDisasmState::Assemble(DebuggerRequest &req) { - auto memLock = Memory::Lock(); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); } diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 8282aac3db..0fc1355ae0 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -291,6 +291,14 @@ void Init() { base, m_pPhysicalRAM, m_pUncachedRAM); } +void Reinit() { + _assert_msg_(SYSTEM, PSP_IsInited(), "Cannot reinit during startup/shutdown"); + Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITING); + Shutdown(); + Init(); + Core_NotifyLifecycle(CoreLifecycle::MEMORY_REINITED); +} + void DoState(PointerWrap &p) { auto s = p.Section("Memory", 1, 3); if (!s) @@ -308,8 +316,7 @@ void DoState(PointerWrap &p) { if (!g_RemasterMode) { g_MemorySize = g_PSPModel == PSP_MODEL_FAT ? RAM_NORMAL_SIZE : RAM_DOUBLE_SIZE; if (oldMemorySize < g_MemorySize) { - Shutdown(); - Init(); + Reinit(); } } } else { @@ -320,8 +327,7 @@ void DoState(PointerWrap &p) { p.DoMarker("PSPModel"); p.Do(g_MemorySize); if (oldMemorySize != g_MemorySize) { - Shutdown(); - Init(); + Reinit(); } } From 5670fc03ae00b817ff0f6a87bb683812df8f7917 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 9 May 2018 17:51:27 -0700 Subject: [PATCH 200/301] Debugger: Add debug interfaces for threads. This way we can switch context. --- Core/Core.vcxproj | 1 + Core/Core.vcxproj.filters | 3 + Core/Debugger/DisassemblyManager.cpp | 42 +++++---- Core/Debugger/DisassemblyManager.h | 14 +-- Core/Debugger/WebSocket/CPUCoreSubscriber.cpp | 85 +++++++++++++----- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 35 ++++++-- Core/HLE/KernelThreadDebugInterface.h | 90 +++++++++++++++++++ Core/HLE/sceKernelThread.cpp | 30 +++++-- Core/HLE/sceKernelThread.h | 2 + 9 files changed, 239 insertions(+), 63 deletions(-) create mode 100644 Core/HLE/KernelThreadDebugInterface.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index a9d703869f..1b95d40373 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -553,6 +553,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index c5fe328767..f4803dd56c 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1337,6 +1337,9 @@ Debugger\WebSocket + + HLE\Kernel + diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 54ffae28d4..2c0a6edd54 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -236,7 +236,7 @@ std::vector DisassemblyManager::getBranchLines(u32 start, u32 size) return result; } -void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest) +void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLineInfo &dest, DebugInterface *cpuDebug) { // This is here really to avoid lock ordering issues. auto memLock = Memory::Lock(); @@ -250,7 +250,7 @@ void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLin if (it != entries.end()) { DisassemblyEntry *entry = it->second; - if (entry->disassemble(address, dest, insertSymbols)) + if (entry->disassemble(address, dest, insertSymbols, cpuDebug)) return; } @@ -432,14 +432,14 @@ u32 DisassemblyFunction::getLineAddress(int line) return lineAddresses[line]; } -bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) { std::lock_guard guard(lock_); auto it = findDisassemblyEntry(entries,address,false); if (it == entries.end()) return false; - return it->second->disassemble(address,dest,insertSymbols); + return it->second->disassemble(address, dest, insertSymbols, cpuDebug); } void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector& dest) @@ -720,16 +720,19 @@ void DisassemblyFunction::clear() hash = 0; } -bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) { + if (!cpuDebug) + cpuDebug = DisassemblyManager::getCpu(); + char opcode[64],arguments[256]; - const char *dizz = DisassemblyManager::getCpu()->disasm(address,4); + const char *dizz = cpuDebug->disasm(address, 4); parseDisasm(dizz,opcode,arguments,insertSymbols); dest.type = DISTYPE_OPCODE; dest.name = opcode; dest.params = arguments; dest.totalSize = 4; - dest.info = MIPSAnalyst::GetOpcodeInfo(DisassemblyManager::getCpu(),address); + dest.info = MIPSAnalyst::GetOpcodeInfo(cpuDebug, address); return true; } @@ -789,11 +792,14 @@ void DisassemblyMacro::setMacroMemory(std::string _name, u32 _immediate, u8 _rt, numOpcodes = 2; } -bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) { + if (!cpuDebug) + cpuDebug = DisassemblyManager::getCpu(); + char buffer[64]; dest.type = DISTYPE_MACRO; - dest.info = MIPSAnalyst::GetOpcodeInfo(DisassemblyManager::getCpu(),address); + dest.info = MIPSAnalyst::GetOpcodeInfo(cpuDebug, address); std::string addressSymbol; switch (type) @@ -802,11 +808,10 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool dest.name = name; addressSymbol = g_symbolMap->GetLabelString(immediate); - if (!addressSymbol.empty() && insertSymbols) - { - sprintf(buffer,"%s,%s",DisassemblyManager::getCpu()->GetRegName(0,rt),addressSymbol.c_str()); + if (!addressSymbol.empty() && insertSymbols) { + sprintf(buffer, "%s,%s", cpuDebug->GetRegName(0, rt), addressSymbol.c_str()); } else { - sprintf(buffer,"%s,0x%08X",DisassemblyManager::getCpu()->GetRegName(0,rt),immediate); + sprintf(buffer, "%s,0x%08X", cpuDebug->GetRegName(0, rt), immediate); } dest.params = buffer; @@ -818,11 +823,10 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool dest.name = name; addressSymbol = g_symbolMap->GetLabelString(immediate); - if (!addressSymbol.empty() && insertSymbols) - { - sprintf(buffer,"%s,%s",DisassemblyManager::getCpu()->GetRegName(0,rt),addressSymbol.c_str()); + if (!addressSymbol.empty() && insertSymbols) { + sprintf(buffer, "%s,%s", cpuDebug->GetRegName(0, rt), addressSymbol.c_str()); } else { - sprintf(buffer,"%s,0x%08X",DisassemblyManager::getCpu()->GetRegName(0,rt),immediate); + sprintf(buffer, "%s,0x%08X", cpuDebug->GetRegName(0, rt), immediate); } dest.params = buffer; @@ -867,7 +871,7 @@ void DisassemblyData::recheck() } } -bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +bool DisassemblyData::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) { dest.type = DISTYPE_DATA; @@ -1065,7 +1069,7 @@ DisassemblyComment::DisassemblyComment(u32 _address, u32 _size, std::string _nam } -bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) +bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo &dest, bool insertSymbols, DebugInterface *cpuDebug) { dest.type = DISTYPE_OTHER; dest.name = name; diff --git a/Core/Debugger/DisassemblyManager.h b/Core/Debugger/DisassemblyManager.h index 7d84805b01..44ae397f6c 100644 --- a/Core/Debugger/DisassemblyManager.h +++ b/Core/Debugger/DisassemblyManager.h @@ -63,7 +63,7 @@ public: virtual int getLineNum(u32 address, bool findStart) = 0; virtual u32 getLineAddress(int line) = 0; virtual u32 getTotalSize() = 0; - virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) = 0; + virtual bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, DebugInterface *cpuDebug) = 0; virtual void getBranchLines(u32 start, u32 size, std::vector& dest) { }; }; @@ -77,7 +77,7 @@ public: int getLineNum(u32 address, bool findStart) override; u32 getLineAddress(int line) override; u32 getTotalSize() override { return size; }; - bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) override; + bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, DebugInterface *cpuDebug) override; void getBranchLines(u32 start, u32 size, std::vector& dest) override; private: @@ -105,7 +105,7 @@ public: int getLineNum(u32 address, bool findStart) override { return (address - this->address) / 4; }; u32 getLineAddress(int line) override { return address + line * 4; }; u32 getTotalSize() override { return num * 4; }; - bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) override; + bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, DebugInterface *cpuDebug) override; void getBranchLines(u32 start, u32 size, std::vector& dest) override; private: @@ -128,7 +128,7 @@ public: int getLineNum(u32 address, bool findStart) override { return 0; }; u32 getLineAddress(int line) override { return address; }; u32 getTotalSize() override { return numOpcodes * 4; }; - bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) override; + bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, DebugInterface *cpuDebug) override; private: enum MacroType { MACRO_LI, MACRO_MEMORYIMM }; @@ -153,7 +153,7 @@ public: int getLineNum(u32 address, bool findStart) override; u32 getLineAddress(int line) override { return lineAddresses[line]; }; u32 getTotalSize() override { return size; }; - bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) override; + bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, DebugInterface *cpuDebug) override; private: void createLines(); @@ -185,7 +185,7 @@ public: int getLineNum(u32 address, bool findStart) override { return 0; }; u32 getLineAddress(int line) override { return address; }; u32 getTotalSize() override { return size; }; - bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols) override; + bool disassemble(u32 address, DisassemblyLineInfo& dest, bool insertSymbols, DebugInterface *cpuDebug) override; private: u32 address; @@ -205,7 +205,7 @@ public: void setCpu(DebugInterface* _cpu) { cpu = _cpu; }; void setMaxParamChars(int num) { maxParamChars = num; clear(); }; - void getLine(u32 address, bool insertSymbols, DisassemblyLineInfo& dest); + void getLine(u32 address, bool insertSymbols, DisassemblyLineInfo &dest, DebugInterface *cpuDebug = nullptr); void analyze(u32 address, u32 size); std::vector getBranchLines(u32 start, u32 size); diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 7b1ffb68ac..0d2c42f66c 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -21,6 +21,7 @@ #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/HLE/sceKernelThread.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSDebugInterface.h" @@ -45,6 +46,20 @@ static std::string RegValueAsFloat(uint32_t u) { return StringFromFormat("%f", bits.f); } +static DebugInterface *CPUFromRequest(DebuggerRequest &req) { + if (!req.HasParam("thread")) + return currentDebugMIPS; + + u32 uid; + if (!req.ParamU32("thread", &uid)) + return nullptr; + + DebugInterface *cpuDebug = KernelDebugThread((SceUID)uid); + if (!cpuDebug) + req.Fail("Thread could not be found"); + return cpuDebug; +} + // Begin stepping and pause the CPU (cpu.stepping) // // No parameters. @@ -100,7 +115,8 @@ void WebSocketCPUStatus(DebuggerRequest &req) { // Retrieve all regs and their values (cpu.getAllRegs) // -// No parameters. +// Parameters: +// - thread: optional number indicating the thread id to get regs for. // // Response (same event name): // - categories: array of objects: @@ -110,19 +126,23 @@ void WebSocketCPUStatus(DebuggerRequest &req) { // - uintValues: array of unsigned integer values for the registers. // - floatValues: array of strings showing float representation. May be "nan", "inf", or "-inf". void WebSocketCPUGetAllRegs(DebuggerRequest &req) { + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; + JsonWriter &json = req.Respond(); json.pushArray("categories"); - for (int c = 0; c < currentDebugMIPS->GetNumCategories(); ++c) { + for (int c = 0; c < cpuDebug->GetNumCategories(); ++c) { json.pushDict(); json.writeInt("id", c); - json.writeString("name", currentDebugMIPS->GetCategoryName(c)); + json.writeString("name", cpuDebug->GetCategoryName(c)); - int total = currentDebugMIPS->GetNumRegsInCategory(c); + int total = cpuDebug->GetNumRegsInCategory(c); json.pushArray("registerNames"); for (int r = 0; r < total; ++r) - json.writeString(currentDebugMIPS->GetRegName(c, r)); + json.writeString(cpuDebug->GetRegName(c, r)); if (c == 0) { json.writeString("pc"); json.writeString("hi"); @@ -133,22 +153,22 @@ void WebSocketCPUGetAllRegs(DebuggerRequest &req) { json.pushArray("uintValues"); // Writing as floating point to avoid negatives. Actually double, so safe. for (int r = 0; r < total; ++r) - json.writeUint(currentDebugMIPS->GetRegValue(c, r)); + json.writeUint(cpuDebug->GetRegValue(c, r)); if (c == 0) { - json.writeUint(currentDebugMIPS->GetPC()); - json.writeUint(currentDebugMIPS->GetHi()); - json.writeUint(currentDebugMIPS->GetLo()); + json.writeUint(cpuDebug->GetPC()); + json.writeUint(cpuDebug->GetHi()); + json.writeUint(cpuDebug->GetLo()); } json.pop(); json.pushArray("floatValues"); // Note: String so it can have Infinity and NaN. for (int r = 0; r < total; ++r) - json.writeString(RegValueAsFloat(currentDebugMIPS->GetRegValue(c, r))); + json.writeString(RegValueAsFloat(cpuDebug->GetRegValue(c, r))); if (c == 0) { - json.writeString(RegValueAsFloat(currentDebugMIPS->GetPC())); - json.writeString(RegValueAsFloat(currentDebugMIPS->GetHi())); - json.writeString(RegValueAsFloat(currentDebugMIPS->GetLo())); + json.writeString(RegValueAsFloat(cpuDebug->GetPC())); + json.writeString(RegValueAsFloat(cpuDebug->GetHi())); + json.writeString(RegValueAsFloat(cpuDebug->GetLo())); } json.pop(); @@ -232,9 +252,11 @@ static DebuggerRegType ValidateCatReg(DebuggerRequest &req, int *cat, int *reg) // Retrieve the value of a single register (cpu.getReg) // // Parameters (by name): +// - thread: optional number indicating the thread id to get from. // - name: string name of register to lookup. // // Parameters (by category id and index, ignored if name specified): +// - thread: optional number indicating the thread id to get from. // - category: id of category for the register. // - register: index into array of registers. // @@ -244,21 +266,25 @@ static DebuggerRegType ValidateCatReg(DebuggerRequest &req, int *cat, int *reg) // - uintValue: value in register. // - floatValue: string showing float representation. May be "nan", "inf", or "-inf". void WebSocketCPUGetReg(DebuggerRequest &req) { + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; + int cat, reg; uint32_t val; switch (ValidateCatReg(req, &cat, ®)) { case DebuggerRegType::NORMAL: - val = currentDebugMIPS->GetRegValue(cat, reg); + val = cpuDebug->GetRegValue(cat, reg); break; case DebuggerRegType::PC: - val = currentDebugMIPS->GetPC(); + val = cpuDebug->GetPC(); break; case DebuggerRegType::HI: - val = currentDebugMIPS->GetHi(); + val = cpuDebug->GetHi(); break; case DebuggerRegType::LO: - val = currentDebugMIPS->GetLo(); + val = cpuDebug->GetLo(); break; case DebuggerRegType::INVALID: @@ -276,11 +302,13 @@ void WebSocketCPUGetReg(DebuggerRequest &req) { // Update the value of a single register (cpu.setReg) // // Parameters (by name): +// - thread: optional number indicating the thread id to update. // - name: string name of register to lookup. // - value: number (uint values only) or string to set to. Values may include // "0x1234", "1.5", "nan", "-inf", etc. For a float, use a string with decimal e.g. "1.0". // // Parameters (by category id and index, ignored if name specified): +// - thread: optional number indicating the thread id to update. // - category: id of category for the register. // - register: index into array of registers. // - value: number (uint values only) or string to set to. Values may include @@ -301,6 +329,10 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { return req.Fail("CPU currently running (cpu.stepping first)"); } + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; + uint32_t val; if (!req.ParamU32("value", &val, true)) { // Already sent error. @@ -313,19 +345,19 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { if (cat == 0 && reg == 0 && val != 0) { return req.Fail("Cannot change reg zero"); } - currentDebugMIPS->SetRegValue(cat, reg, val); + cpuDebug->SetRegValue(cat, reg, val); // In case part of it was ignored (e.g. flags reg.) - val = currentDebugMIPS->GetRegValue(cat, reg); + val = cpuDebug->GetRegValue(cat, reg); break; case DebuggerRegType::PC: - currentDebugMIPS->SetPC(val); + cpuDebug->SetPC(val); break; case DebuggerRegType::HI: - currentDebugMIPS->SetHi(val); + cpuDebug->SetHi(val); break; case DebuggerRegType::LO: - currentDebugMIPS->SetLo(val); + cpuDebug->SetLo(val); break; case DebuggerRegType::INVALID: @@ -344,6 +376,7 @@ void WebSocketCPUSetReg(DebuggerRequest &req) { // Evaluate an expression (cpu.evaluate) // // Parameters: +// - thread: optional number indicating the thread id to update. // - expression: string containing labels, operators, regs, etc. // // Response (same event name): @@ -354,6 +387,10 @@ void WebSocketCPUEvaluate(DebuggerRequest &req) { return req.Fail("CPU not started"); } + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; + std::string exp; if (!req.ParamString("expression", &exp)) { // Already sent error. @@ -362,10 +399,10 @@ void WebSocketCPUEvaluate(DebuggerRequest &req) { u32 val; PostfixExpression postfix; - if (!currentDebugMIPS->initExpression(exp.c_str(), postfix)) { + if (!cpuDebug->initExpression(exp.c_str(), postfix)) { return req.Fail(StringFromFormat("Could not parse expression syntax: %s", getExpressionError())); } - if (!currentDebugMIPS->parseExpression(postfix, val)) { + if (!cpuDebug->parseExpression(postfix, val)) { return req.Fail(StringFromFormat("Could not evaluate expression: %s", getExpressionError())); } diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 95dddcebb8..9971505a84 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -23,6 +23,7 @@ #include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/HLE/sceKernelThread.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSAsm.h" #include "Core/MIPS/MIPSDebugInterface.h" @@ -62,6 +63,20 @@ void WebSocketDisasmShutdown(void *p) { delete static_cast(p); } +static DebugInterface *CPUFromRequest(DebuggerRequest &req) { + if (!req.HasParam("thread")) + return currentDebugMIPS; + + u32 uid; + if (!req.ParamU32("thread", &uid)) + return nullptr; + + DebugInterface *cpuDebug = KernelDebugThread((SceUID)uid); + if (!cpuDebug) + req.Fail("Thread could not be found"); + return cpuDebug; +} + void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l) { u32 addr = l.info.opcodeAddress; json.pushDict(); @@ -112,6 +127,7 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi json.writeNull("breakpoint"); } + // This is always the current execution's PC. json.writeBool("isCurrentPC", currentDebugMIPS->GetPC() == addr); if (l.info.isBranch) { json.pushDict("branch"); @@ -229,11 +245,13 @@ void WebSocketDisasmState::Base(DebuggerRequest &req) { // Disassemble a range of memory as CPU instructions (memory.disasm) // // Parameters (by count): +// - thread: optional number indicating the thread id for branch info. // - address: number specifying the start address. // - count: number of lines to return (may be clamped to an internal limit.) // - displaySymbols: boolean true to show symbol names in instruction params. // // Parameters (by end address): +// - thread: optional number indicating the thread id for branch info. // - address: number specifying the start address. // - end: number which must be after the start address (may be clamped to an internal limit.) // - displaySymbols: boolean true to show symbol names in instruction params. @@ -256,9 +274,11 @@ void WebSocketDisasmState::Base(DebuggerRequest &req) { // - params: formatted parameters for the instruction. // - (other info about the disassembled line.) void WebSocketDisasmState::Disasm(DebuggerRequest &req) { - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); - } + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; // In case of client errors, we limit the range to something that won't make us crash. static const uint32_t MAX_RANGE = 10000; @@ -310,7 +330,7 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { DisassemblyLineInfo line; uint32_t addr = start; for (uint32_t i = 0; i < count; ++i) { - disasm_.getLine(addr, displaySymbols, line); + disasm_.getLine(addr, displaySymbols, line, cpuDebug); WriteDisasmLine(json, line); addr += line.totalSize; @@ -330,6 +350,7 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { // Search disassembly for some text (cpu.searchDisasm) // // Parameters: +// - thread: optional number indicating the thread id (may not affect search much.) // - address: starting address as a number. // - end: optional end address as a number (otherwise uses start.) // - match: string to search for. @@ -338,9 +359,11 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { // Response (same event name): // - address: number address of match or null if none was found. void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); - } + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; uint32_t start; if (!req.ParamU32("address", &start)) @@ -373,7 +396,7 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { bool found = false; uint32_t addr = start; do { - disasm_.getLine(addr, displaySymbols, line); + disasm_.getLine(addr, displaySymbols, line, cpuDebug); const std::string addressSymbol = g_symbolMap->GetLabelString(addr); std::string mergeForSearch; diff --git a/Core/HLE/KernelThreadDebugInterface.h b/Core/HLE/KernelThreadDebugInterface.h new file mode 100644 index 0000000000..012a93fc37 --- /dev/null +++ b/Core/HLE/KernelThreadDebugInterface.h @@ -0,0 +1,90 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include "Core/HLE/sceKernelThread.h" +#include "Core/MIPS/MIPSDebugInterface.h" + +class KernelThreadDebugInterface : public MIPSDebugInterface { +public: + KernelThreadDebugInterface(MIPSState *c, ThreadContext &t) : MIPSDebugInterface(c), ctx(t) { + } + + unsigned int getPC() override { return ctx.pc; } + void setPC(unsigned int address) override { ctx.pc = address; } + + u32 GetGPR32Value(int reg) override { return ctx.r[reg]; } + u32 GetPC() override { return ctx.pc; } + u32 GetLR() override { return ctx.r[MIPS_REG_RA]; } + void SetPC(u32 _pc) override { ctx.pc = _pc; } + + void PrintRegValue(int cat, int index, char *out) override { + switch (cat) { + case 0: sprintf(out, "%08X", ctx.r[index]); break; + case 1: sprintf(out, "%f", ctx.f[index]); break; + case 2: sprintf(out, "N/A"); break; + } + } + + u32 GetHi() override { + return ctx.hi; + } + + u32 GetLo() override { + return ctx.lo; + } + + void SetHi(u32 val) override { + ctx.hi = val; + } + + void SetLo(u32 val) override { + ctx.lo = val; + } + + u32 GetRegValue(int cat, int index) override { + switch (cat) { + case 0: return ctx.r[index]; + case 1: return ctx.fi[index]; + case 2: return ctx.vi[voffset[index]]; + default: return 0; + } + } + + void SetRegValue(int cat, int index, u32 value) override { + switch (cat) { + case 0: + if (index != 0) + ctx.r[index] = value; + break; + + case 1: + ctx.fi[index] = value; + break; + + case 2: + ctx.vi[voffset[index]] = value; + break; + + default: + break; + } + } + +protected: + ThreadContext &ctx; +}; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index fccf608db1..11d246576e 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -29,6 +29,7 @@ #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSDebugInterface.h" #include "Core/CoreTiming.h" #include "Core/MemMapHelpers.h" #include "Core/MIPS/JitCommon/JitCommon.h" @@ -41,6 +42,7 @@ #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelModule.h" #include "Core/HLE/sceKernelInterrupt.h" +#include "Core/HLE/KernelThreadDebugInterface.h" #include "Core/HLE/KernelWaitHelpers.h" #include "Core/HLE/ThreadQueueList.h" @@ -493,7 +495,7 @@ public: return true; } - Thread() + Thread() : debug(currentMIPS, context) { currentStack.start = 0; } @@ -586,6 +588,7 @@ public: SceUID currentCallbackId; ThreadContext context; + KernelThreadDebugInterface debug; std::vector callbacks; @@ -3510,14 +3513,13 @@ std::vector GetThreadsInfo() std::vector threadList; u32 error; - for (auto iter = threadqueue.begin(); iter != threadqueue.end(); ++iter) - { - Thread *t = kernelObjects.Get(*iter, error); + for (const auto uid : threadqueue) { + Thread *t = kernelObjects.Get(uid, error); if (!t) continue; DebugThreadInfo info; - info.id = *iter; + info.id = uid; strncpy(info.name,t->GetName(),KERNELOBJECT_MAX_NAME_LENGTH); info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; info.status = t->nt.status; @@ -3526,17 +3528,31 @@ std::vector GetThreadsInfo() info.stackSize = (u32)t->nt.stackSize; info.priority = t->nt.currentPriority; info.waitType = (WaitType)(u32)t->nt.waitType; - if(*iter == currentThread) + info.isCurrent = uid == currentThread; + if (info.isCurrent) info.curPC = currentMIPS->pc; else info.curPC = t->context.pc; - info.isCurrent = (*iter == currentThread); threadList.push_back(info); } return threadList; } +DebugInterface *KernelDebugThread(SceUID threadID) { + if (threadID == currentThread) { + return currentDebugMIPS; + } + + u32 error; + Thread *t = kernelObjects.Get(threadID, error); + if (t) { + return &t->debug; + } + + return nullptr; +} + void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus) { u32 error; diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 53fa5e4367..6216fc02ec 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -27,6 +27,7 @@ // http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/ThreadManForUser.java class Thread; +class DebugInterface; int sceKernelChangeThreadPriority(SceUID threadID, int priority); SceUID __KernelCreateThreadInternal(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr); @@ -316,6 +317,7 @@ struct DebugThreadInfo }; std::vector GetThreadsInfo(); +DebugInterface *KernelDebugThread(SceUID threadID); void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus); int LoadExecForUser_362A956B(); From 5bfba9b28440d0b5afcc26c3053369abb0e6c570 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 8 May 2018 17:23:14 -0700 Subject: [PATCH 201/301] Debugger: Add HLE API funcs. Lumping function symbols in here too, they're pretty related... --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 ++ Core/Debugger/WebSocket.cpp | 2 + Core/Debugger/WebSocket/HLESubscriber.cpp | 103 ++++++++++++++++++++++ Core/Debugger/WebSocket/HLESubscriber.h | 26 ++++++ Core/HLE/sceKernelThread.cpp | 28 +++--- android/jni/Android.mk | 1 + 8 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 Core/Debugger/WebSocket/HLESubscriber.cpp create mode 100644 Core/Debugger/WebSocket/HLESubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a1cc6b95f5..5d1b359c53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1414,6 +1414,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/GameBroadcaster.h Core/Debugger/WebSocket/GameSubscriber.cpp Core/Debugger/WebSocket/GameSubscriber.h + Core/Debugger/WebSocket/HLESubscriber.cpp + Core/Debugger/WebSocket/HLESubscriber.h Core/Debugger/WebSocket/LogBroadcaster.cpp Core/Debugger/WebSocket/LogBroadcaster.h Core/Debugger/WebSocket/SteppingBroadcaster.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 1b95d40373..d38b535a23 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -189,6 +189,7 @@ + @@ -546,6 +547,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index f4803dd56c..221dd1c738 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -725,6 +725,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1340,6 +1343,9 @@ HLE\Kernel + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index f477746c78..7bce20cc5e 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -52,6 +52,7 @@ #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h" +#include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/SteppingSubscriber.h" typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); @@ -66,6 +67,7 @@ static const std::vector subscribers({ { &WebSocketCPUCoreInit, nullptr }, { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, { &WebSocketGameInit, nullptr }, + { &WebSocketHLEInit, nullptr }, { &WebSocketSteppingInit, &WebSocketSteppingShutdown }, }); diff --git a/Core/Debugger/WebSocket/HLESubscriber.cpp b/Core/Debugger/WebSocket/HLESubscriber.cpp new file mode 100644 index 0000000000..efb82c3b36 --- /dev/null +++ b/Core/Debugger/WebSocket/HLESubscriber.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Debugger/SymbolMap.h" +#include "Core/Debugger/WebSocket/HLESubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/HLE/sceKernelThread.h" + +void *WebSocketHLEInit(DebuggerEventHandlerMap &map) { + map["hle.thread.list"] = &WebSocketHLEThreadList; + map["hle.func.list"] = &WebSocketHLEFuncList; + map["hle.module.list"] = &WebSocketHLEModuleList; + + return nullptr; +} + +void WebSocketHLEThreadList(DebuggerRequest &req) { + // Will just return none of the CPU isn't ready yet. + auto threads = GetThreadsInfo(); + + JsonWriter &json = req.Respond(); + json.pushArray("threads"); + for (auto th : threads) { + json.pushDict(); + json.writeUint("id", th.id); + json.writeString("name", th.name); + json.writeInt("status", th.status); + json.pushArray("statuses"); + if (th.status & THREADSTATUS_RUNNING) + json.writeString("running"); + if (th.status & THREADSTATUS_READY) + json.writeString("ready"); + if (th.status & THREADSTATUS_WAIT) + json.writeString("wait"); + if (th.status & THREADSTATUS_SUSPEND) + json.writeString("suspend"); + if (th.status & THREADSTATUS_DORMANT) + json.writeString("dormant"); + if (th.status & THREADSTATUS_DEAD) + json.writeString("dead"); + json.pop(); + json.writeUint("pc", th.curPC); + json.writeUint("entry", th.entrypoint); + json.writeUint("initialStackSize", th.initialStack); + json.writeUint("currentStackSize", th.stackSize); + json.writeInt("priority", th.priority); + json.writeInt("priority", (int)th.waitType); + json.writeBool("isCurrent", th.isCurrent); + json.pop(); + } + json.pop(); +} + +void WebSocketHLEFuncList(DebuggerRequest &req) { + if (!g_symbolMap) + return req.Fail("CPU not active"); + + auto functions = g_symbolMap->GetAllSymbols(ST_FUNCTION); + + JsonWriter &json = req.Respond(); + json.pushArray("functions"); + for (auto f : functions) { + json.pushDict(); + json.writeString("name", f.name); + json.writeUint("address", f.address); + json.writeUint("size", f.size); + json.pop(); + } + json.pop(); +} + +void WebSocketHLEModuleList(DebuggerRequest &req) { + if (!g_symbolMap) + return req.Fail("CPU not active"); + + auto modules = g_symbolMap->getAllModules(); + + JsonWriter &json = req.Respond(); + json.pushArray("functions"); + for (auto m : modules) { + json.pushDict(); + json.writeString("name", m.name); + json.writeUint("address", m.address); + json.writeUint("size", m.size); + json.writeBool("isActive", m.active); + json.pop(); + } + json.pop(); +} diff --git a/Core/Debugger/WebSocket/HLESubscriber.h b/Core/Debugger/WebSocket/HLESubscriber.h new file mode 100644 index 0000000000..61af4dc80d --- /dev/null +++ b/Core/Debugger/WebSocket/HLESubscriber.h @@ -0,0 +1,26 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketHLEInit(DebuggerEventHandlerMap &map); + +void WebSocketHLEThreadList(DebuggerRequest &req); +void WebSocketHLEFuncList(DebuggerRequest &req); +void WebSocketHLEModuleList(DebuggerRequest &req); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 11d246576e..576c3f1182 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -15,10 +15,11 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include -#include -#include #include +#include +#include +#include +#include #include "base/logging.h" @@ -647,6 +648,8 @@ static std::vector pendingDeleteThreads; // Lists all thread ids that aren't deleted/etc. std::vector threadqueue; +// Only for debugger, so not needed to read, just write. +std::mutex threadqueueLock; // Lists only ready thread ids. ThreadQueueList threadReadyQueue; @@ -1154,8 +1157,9 @@ void __KernelIdle() __KernelReSchedule("idle"); } -void __KernelThreadingShutdown() -{ +void __KernelThreadingShutdown() { + std::lock_guard guard(threadqueueLock); + kernelMemory.Free(threadReturnHackAddr); threadqueue.clear(); threadReadyQueue.clear(); @@ -1590,8 +1594,9 @@ void __KernelCancelThreadEndTimeout(SceUID threadID) CoreTiming::UnscheduleEvent(eventThreadEndTimeout, threadID); } -static void __KernelRemoveFromThreadQueue(SceUID threadID) -{ +static void __KernelRemoveFromThreadQueue(SceUID threadID) { + std::lock_guard guard(threadqueueLock); + int prio = __KernelGetThreadPrio(threadID); if (prio != 0) threadReadyQueue.remove(prio, threadID); @@ -1850,8 +1855,9 @@ void __KernelResetThread(Thread *t, int lowestPriority) ERROR_LOG_REPORT(SCEKERNEL, "Resetting thread with threads waiting on end?"); } -Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr) -{ +Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr) { + std::lock_guard guard(threadqueueLock); + Thread *t = new Thread; id = kernelObjects.Create(t); @@ -3508,8 +3514,8 @@ void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFun waitTypeFuncs[type].endFunc = endFunc; } -std::vector GetThreadsInfo() -{ +std::vector GetThreadsInfo() { + std::lock_guard guard(threadqueueLock); std::vector threadList; u32 error; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b08ade3a28..4501775884 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -307,6 +307,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/DisasmSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ + $(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingSubscriber.cpp \ From b828497fe2a12caff429f0eedcb1799d6dbe9a08 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 11 May 2018 20:58:36 -0700 Subject: [PATCH 202/301] Debugger: Avoid some lock ordering issues. Ideally get rid of the memory lock... --- Core/Debugger/DisassemblyManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 2c0a6edd54..c520f86f5f 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -150,6 +150,7 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024) if (!PSP_IsInited()) return; + auto memLock = Memory::Lock(); std::lock_guard guard(entriesLock_); auto it = findDisassemblyEntry(entries, address, false); if (it != entries.end()) @@ -272,6 +273,7 @@ void DisassemblyManager::getLine(u32 address, bool insertSymbols, DisassemblyLin u32 DisassemblyManager::getStartAddress(u32 address) { + auto memLock = Memory::Lock(); std::lock_guard guard(entriesLock_); auto it = findDisassemblyEntry(entries,address,false); if (it == entries.end()) @@ -289,6 +291,7 @@ u32 DisassemblyManager::getStartAddress(u32 address) u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n) { + auto memLock = Memory::Lock(); std::lock_guard guard(entriesLock_); while (Memory::IsValidAddress(address)) { @@ -318,6 +321,7 @@ u32 DisassemblyManager::getNthPreviousAddress(u32 address, int n) u32 DisassemblyManager::getNthNextAddress(u32 address, int n) { + auto memLock = Memory::Lock(); std::lock_guard guard(entriesLock_); while (Memory::IsValidAddress(address)) { @@ -352,6 +356,7 @@ DisassemblyManager::~DisassemblyManager() { void DisassemblyManager::clear() { + auto memLock = Memory::Lock(); std::lock_guard guard(entriesLock_); for (auto it = entries.begin(); it != entries.end(); it++) { From a863ce79adc7c74c69f893d2471c9387cb0b63ec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 11 May 2018 21:16:03 -0700 Subject: [PATCH 203/301] Debugger: Allow stepping based on thread. --- .../Debugger/WebSocket/SteppingSubscriber.cpp | 121 +++++++++++++----- 1 file changed, 88 insertions(+), 33 deletions(-) diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index 062064d9cd..594726e2e2 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -43,8 +43,8 @@ struct WebSocketSteppingState { void HLE(DebuggerRequest &req); protected: - u32 GetNextAddress(); - int GetNextInstructionCount(); + u32 GetNextAddress(DebugInterface *cpuDebug); + int GetNextInstructionCount(DebugInterface *cpuDebug); void PrepareResume(); DisassemblyManager disasm_; @@ -65,27 +65,63 @@ void WebSocketSteppingShutdown(void *p) { delete static_cast(p); } -// Single step into the next instruction (cpu.stepInto) -// -// No parameters. -// -// No immediate response. A cpu.stepping event will be sent once complete. -void WebSocketSteppingState::Into(DebuggerRequest &req) { - if (!currentDebugMIPS->isAlive()) { - return req.Fail("CPU not started"); +static DebugInterface *CPUFromRequest(DebuggerRequest &req, u32 *threadID = nullptr) { + if (!req.HasParam("thread")) { + if (threadID) + *threadID = -1; + return currentDebugMIPS; } + u32 uid; + if (!req.ParamU32("thread", &uid)) + return nullptr; + + DebugInterface *cpuDebug = KernelDebugThread((SceUID)uid); + if (!cpuDebug) + req.Fail("Thread could not be found"); + if (threadID) + *threadID = uid; + return cpuDebug; +} + +// Single step into the next instruction (cpu.stepInto) +// +// Parameters: +// - thread: optional number indicating the thread id to plan stepping on. +// +// No immediate response. A cpu.stepping event will be sent once complete. +// +// Note: any thread can wake the cpu when it hits the next instruction currently. +void WebSocketSteppingState::Into(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) + return req.Fail("CPU not started"); if (!Core_IsStepping()) { Core_EnableStepping(true); return; } - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(currentMIPS->pc); + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; - int c = GetNextInstructionCount(); - for (int i = 0; i < c; ++i) { - Core_DoSingleStep(); + if (cpuDebug == currentDebugMIPS) { + // If the current PC is on a breakpoint, the user doesn't want to do nothing. + CBreakPoints::SetSkipFirst(currentMIPS->pc); + + int c = GetNextInstructionCount(cpuDebug); + for (int i = 0; i < c; ++i) { + Core_DoSingleStep(); + } + } else { + u32 breakpointAddress = cpuDebug->GetPC(); + PrepareResume(); + // Could have advanced to the breakpoint already in PrepareResume(). + // Note: we need to get cpuDebug again anyway (in case we ran some HLE above.) + cpuDebug = CPUFromRequest(req); + if (cpuDebug != currentDebugMIPS) { + CBreakPoints::AddBreakPoint(breakpointAddress, true); + Core_EnableStepping(false); + } } } @@ -93,16 +129,23 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) { // // Note: this jumps over function calls, but also delay slots. // -// No parameters. +// Parameters: +// - thread: optional number indicating the thread id to plan stepping on. // // No immediate response. A cpu.stepping event will be sent once complete. +// +// Note: any thread can wake the cpu when it hits the next instruction currently. void WebSocketSteppingState::Over(DebuggerRequest &req) { - if (!currentDebugMIPS->isAlive()) { + if (!currentDebugMIPS->isAlive()) return req.Fail("CPU not started"); - } + if (!Core_IsStepping()) + return req.Fail("CPU currently running (cpu.stepping first)"); + auto cpuDebug = CPUFromRequest(req); + if (!cpuDebug) + return; - MipsOpcodeInfo info = GetOpcodeInfo(currentDebugMIPS, currentMIPS->pc); - u32 breakpointAddress = GetNextAddress(); + MipsOpcodeInfo info = GetOpcodeInfo(cpuDebug, cpuDebug->GetPC()); + u32 breakpointAddress = GetNextAddress(cpuDebug); if (info.isBranch) { if (info.isConditional && !info.isLinkedBranch) { if (info.conditionMet) { @@ -124,7 +167,8 @@ void WebSocketSteppingState::Over(DebuggerRequest &req) { PrepareResume(); // Could have advanced to the breakpoint already in PrepareResume(). - if (currentMIPS->pc != breakpointAddress) { + cpuDebug = CPUFromRequest(req); + if (cpuDebug->GetPC() != breakpointAddress) { CBreakPoints::AddBreakPoint(breakpointAddress, true); Core_EnableStepping(false); } @@ -132,26 +176,36 @@ void WebSocketSteppingState::Over(DebuggerRequest &req) { // Step out of a function based on a stack walk (cpu.stepOut) // -// No parameters. +// Parameters: +// - thread: optional number indicating the thread id to plan stepping on. // // No immediate response. A cpu.stepping event will be sent once complete. +// +// Note: any thread can wake the cpu when it hits the next instruction currently. void WebSocketSteppingState::Out(DebuggerRequest &req) { - if (!currentDebugMIPS->isAlive()) { + if (!currentDebugMIPS->isAlive()) return req.Fail("CPU not started"); - } + if (!Core_IsStepping()) + return req.Fail("CPU currently running (cpu.stepping first)"); + u32 threadID; + auto cpuDebug = CPUFromRequest(req, &threadID); + if (!cpuDebug) + return; auto threads = GetThreadsInfo(); - u32 entry = currentMIPS->pc; + u32 entry = cpuDebug->GetPC(); u32 stackTop = 0; for (const DebugThreadInfo &th : threads) { - if (th.isCurrent) { + if ((threadID == -1 && th.isCurrent) || th.id == threadID) { entry = th.entrypoint; stackTop = th.initialStack; break; } } - auto frames = MIPSStackWalk::Walk(currentMIPS->pc, currentMIPS->r[MIPS_REG_RA], currentMIPS->r[MIPS_REG_SP], entry, stackTop); + u32 ra = cpuDebug->GetRegValue(0, MIPS_REG_RA); + u32 sp = cpuDebug->GetRegValue(0, MIPS_REG_SP); + auto frames = MIPSStackWalk::Walk(cpuDebug->GetPC(), ra, sp, entry, stackTop); if (frames.size() < 2) { // TODO: Respond in some way? return; @@ -160,13 +214,14 @@ void WebSocketSteppingState::Out(DebuggerRequest &req) { u32 breakpointAddress = frames[1].pc; PrepareResume(); // Could have advanced to the breakpoint already in PrepareResume(). - if (currentMIPS->pc != breakpointAddress) { + cpuDebug = CPUFromRequest(req); + if (cpuDebug->GetPC() != breakpointAddress) { CBreakPoints::AddBreakPoint(breakpointAddress, true); Core_EnableStepping(false); } } -// Run until a certain address (cpu.stepOut) +// Run until a certain address (cpu.runUntil) // // Parameters: // - address: number parameter for destination. @@ -207,13 +262,13 @@ void WebSocketSteppingState::HLE(DebuggerRequest &req) { Core_EnableStepping(false); } -u32 WebSocketSteppingState::GetNextAddress() { - u32 current = disasm_.getStartAddress(currentMIPS->pc); +u32 WebSocketSteppingState::GetNextAddress(DebugInterface *cpuDebug) { + u32 current = disasm_.getStartAddress(cpuDebug->GetPC()); return disasm_.getNthNextAddress(current, 1); } -int WebSocketSteppingState::GetNextInstructionCount() { - return (GetNextAddress() - currentMIPS->pc) / 4; +int WebSocketSteppingState::GetNextInstructionCount(DebugInterface *cpuDebug) { + return (GetNextAddress(cpuDebug) - cpuDebug->GetPC()) / 4; } void WebSocketSteppingState::PrepareResume() { From b11465632192a11b639f100bf489768b6e7a3de3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 12 May 2018 06:11:55 -0700 Subject: [PATCH 204/301] Debugger: Allow conditions on threadID/moduleID. And now step over/out/into can tie to the correct thread. --- Core/Debugger/Breakpoints.cpp | 6 +-- Core/Debugger/Breakpoints.h | 2 +- .../Debugger/WebSocket/SteppingSubscriber.cpp | 52 ++++++++++++------- Core/MIPS/MIPSDebugInterface.cpp | 19 ++++++- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index d1b293c578..dd6184d2ce 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -297,7 +297,7 @@ void CBreakPoints::ClearTemporaryBreakPoints() void CBreakPoints::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond) { std::unique_lock guard(breakPointsMutex_); - size_t bp = FindBreakpoint(addr, true, false); + size_t bp = FindBreakpoint(addr); if (bp != INVALID_BREAKPOINT) { breakPoints_[bp].hasCond = true; @@ -310,7 +310,7 @@ void CBreakPoints::ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond) void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr) { std::unique_lock guard(breakPointsMutex_); - size_t bp = FindBreakpoint(addr, true, false); + size_t bp = FindBreakpoint(addr); if (bp != INVALID_BREAKPOINT) { breakPoints_[bp].hasCond = false; @@ -322,7 +322,7 @@ void CBreakPoints::ChangeBreakPointRemoveCond(u32 addr) BreakPointCond *CBreakPoints::GetBreakPointCondition(u32 addr) { std::lock_guard guard(breakPointsMutex_); - size_t bp = FindBreakpoint(addr, true, false); + size_t bp = FindBreakpoint(addr); if (bp != INVALID_BREAKPOINT && breakPoints_[bp].hasCond) return &breakPoints_[bp].cond; return NULL; diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 45f91d3cc8..bd3dd670e4 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -133,7 +133,7 @@ public: static void ClearAllBreakPoints(); static void ClearTemporaryBreakPoints(); - // Makes a copy. Temporary breakpoints can't have conditions. + // Makes a copy of the condition. static void ChangeBreakPointAddCond(u32 addr, const BreakPointCond &cond); static void ChangeBreakPointRemoveCond(u32 addr); static BreakPointCond *GetBreakPointCondition(u32 addr); diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index 594726e2e2..e1347f3a14 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -43,9 +43,10 @@ struct WebSocketSteppingState { void HLE(DebuggerRequest &req); protected: - u32 GetNextAddress(DebugInterface *cpuDebug); + uint32_t GetNextAddress(DebugInterface *cpuDebug); int GetNextInstructionCount(DebugInterface *cpuDebug); void PrepareResume(); + void AddThreadCondition(uint32_t breakpointAddress, uint32_t threadID); DisassemblyManager disasm_; }; @@ -65,14 +66,14 @@ void WebSocketSteppingShutdown(void *p) { delete static_cast(p); } -static DebugInterface *CPUFromRequest(DebuggerRequest &req, u32 *threadID = nullptr) { +static DebugInterface *CPUFromRequest(DebuggerRequest &req, uint32_t *threadID = nullptr) { if (!req.HasParam("thread")) { if (threadID) *threadID = -1; return currentDebugMIPS; } - u32 uid; + uint32_t uid; if (!req.ParamU32("thread", &uid)) return nullptr; @@ -100,7 +101,8 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) { return; } - auto cpuDebug = CPUFromRequest(req); + uint32_t threadID; + auto cpuDebug = CPUFromRequest(req, &threadID); if (!cpuDebug) return; @@ -113,13 +115,14 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) { Core_DoSingleStep(); } } else { - u32 breakpointAddress = cpuDebug->GetPC(); + uint32_t breakpointAddress = cpuDebug->GetPC(); PrepareResume(); // Could have advanced to the breakpoint already in PrepareResume(). // Note: we need to get cpuDebug again anyway (in case we ran some HLE above.) cpuDebug = CPUFromRequest(req); if (cpuDebug != currentDebugMIPS) { CBreakPoints::AddBreakPoint(breakpointAddress, true); + AddThreadCondition(breakpointAddress, threadID); Core_EnableStepping(false); } } @@ -140,12 +143,14 @@ void WebSocketSteppingState::Over(DebuggerRequest &req) { return req.Fail("CPU not started"); if (!Core_IsStepping()) return req.Fail("CPU currently running (cpu.stepping first)"); - auto cpuDebug = CPUFromRequest(req); + + uint32_t threadID; + auto cpuDebug = CPUFromRequest(req, &threadID); if (!cpuDebug) return; MipsOpcodeInfo info = GetOpcodeInfo(cpuDebug, cpuDebug->GetPC()); - u32 breakpointAddress = GetNextAddress(cpuDebug); + uint32_t breakpointAddress = GetNextAddress(cpuDebug); if (info.isBranch) { if (info.isConditional && !info.isLinkedBranch) { if (info.conditionMet) { @@ -170,6 +175,8 @@ void WebSocketSteppingState::Over(DebuggerRequest &req) { cpuDebug = CPUFromRequest(req); if (cpuDebug->GetPC() != breakpointAddress) { CBreakPoints::AddBreakPoint(breakpointAddress, true); + if (cpuDebug != currentDebugMIPS) + AddThreadCondition(breakpointAddress, threadID); Core_EnableStepping(false); } } @@ -187,14 +194,15 @@ void WebSocketSteppingState::Out(DebuggerRequest &req) { return req.Fail("CPU not started"); if (!Core_IsStepping()) return req.Fail("CPU currently running (cpu.stepping first)"); - u32 threadID; + + uint32_t threadID; auto cpuDebug = CPUFromRequest(req, &threadID); if (!cpuDebug) return; auto threads = GetThreadsInfo(); - u32 entry = cpuDebug->GetPC(); - u32 stackTop = 0; + uint32_t entry = cpuDebug->GetPC(); + uint32_t stackTop = 0; for (const DebugThreadInfo &th : threads) { if ((threadID == -1 && th.isCurrent) || th.id == threadID) { entry = th.entrypoint; @@ -203,20 +211,21 @@ void WebSocketSteppingState::Out(DebuggerRequest &req) { } } - u32 ra = cpuDebug->GetRegValue(0, MIPS_REG_RA); - u32 sp = cpuDebug->GetRegValue(0, MIPS_REG_SP); + uint32_t ra = cpuDebug->GetRegValue(0, MIPS_REG_RA); + uint32_t sp = cpuDebug->GetRegValue(0, MIPS_REG_SP); auto frames = MIPSStackWalk::Walk(cpuDebug->GetPC(), ra, sp, entry, stackTop); if (frames.size() < 2) { - // TODO: Respond in some way? - return; + return req.Fail("Could not find function call to step out into"); } - u32 breakpointAddress = frames[1].pc; + uint32_t breakpointAddress = frames[1].pc; PrepareResume(); // Could have advanced to the breakpoint already in PrepareResume(). cpuDebug = CPUFromRequest(req); if (cpuDebug->GetPC() != breakpointAddress) { CBreakPoints::AddBreakPoint(breakpointAddress, true); + if (cpuDebug != currentDebugMIPS) + AddThreadCondition(breakpointAddress, threadID); Core_EnableStepping(false); } } @@ -232,7 +241,7 @@ void WebSocketSteppingState::RunUntil(DebuggerRequest &req) { return req.Fail("CPU not started"); } - u32 address = 0; + uint32_t address = 0; if (!req.ParamU32("address", &address)) { // Error already sent. return; @@ -262,8 +271,8 @@ void WebSocketSteppingState::HLE(DebuggerRequest &req) { Core_EnableStepping(false); } -u32 WebSocketSteppingState::GetNextAddress(DebugInterface *cpuDebug) { - u32 current = disasm_.getStartAddress(cpuDebug->GetPC()); +uint32_t WebSocketSteppingState::GetNextAddress(DebugInterface *cpuDebug) { + uint32_t current = disasm_.getStartAddress(cpuDebug->GetPC()); return disasm_.getNthNextAddress(current, 1); } @@ -280,3 +289,10 @@ void WebSocketSteppingState::PrepareResume() { } } +void WebSocketSteppingState::AddThreadCondition(uint32_t breakpointAddress, uint32_t threadID) { + BreakPointCond cond; + cond.debug = currentDebugMIPS; + cond.expressionString = StringFromFormat("threadid == 0x%08x", threadID); + if (currentDebugMIPS->initExpression(cond.expressionString.c_str(), cond.expression)) + CBreakPoints::ChangeBreakPointAddCond(breakpointAddress, cond); +} diff --git a/Core/MIPS/MIPSDebugInterface.cpp b/Core/MIPS/MIPSDebugInterface.cpp index 943e564938..ae1a9a7a6c 100644 --- a/Core/MIPS/MIPSDebugInterface.cpp +++ b/Core/MIPS/MIPSDebugInterface.cpp @@ -23,6 +23,7 @@ #include "Core/Debugger/DebugInterface.h" #include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/HLE/sceKernelThread.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPS.h" @@ -37,13 +38,16 @@ enum ReferenceIndexType { REF_INDEX_VFPU = 0x4000, REF_INDEX_VFPU_INT = 0x8000, REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU, + REF_INDEX_HLE = 0x10000, + REF_INDEX_THREAD = REF_INDEX_HLE | 0, + REF_INDEX_MODULE = REF_INDEX_HLE | 1, }; class MipsExpressionFunctions: public IExpressionFunctions { public: - MipsExpressionFunctions(DebugInterface* cpu): cpu(cpu) { }; + MipsExpressionFunctions(DebugInterface* cpu): cpu(cpu) { } bool parseReference(char* str, uint32_t& referenceIndex) override { @@ -106,6 +110,15 @@ public: return true; } + if (strcasecmp(str, "threadid") == 0) { + referenceIndex = REF_INDEX_THREAD; + return true; + } + if (strcasecmp(str, "moduleid") == 0) { + referenceIndex = REF_INDEX_MODULE; + return true; + } + return false; } @@ -124,6 +137,10 @@ public: return cpu->GetHi(); if (referenceIndex == REF_INDEX_LO) return cpu->GetLo(); + if (referenceIndex == REF_INDEX_THREAD) + return __KernelGetCurThread(); + if (referenceIndex == REF_INDEX_MODULE) + return __KernelGetCurThreadModuleId(); if ((referenceIndex & ~(REF_INDEX_FPU | REF_INDEX_FPU_INT)) < 32) return cpu->GetRegValue(1, referenceIndex & ~(REF_INDEX_FPU | REF_INDEX_FPU_INT)); if ((referenceIndex & ~(REF_INDEX_VFPU | REF_INDEX_VFPU_INT)) < 128) From 25085fa39477c3b1ea2fb2648bf926fe8bb2e156 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 May 2018 10:45:12 -0700 Subject: [PATCH 205/301] Debugger: Add func name and data symbol to disasm. --- Core/Debugger/WebSocket/DisasmSubscriber.cpp | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 9971505a84..78ee607fdf 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -112,6 +112,30 @@ void WebSocketDisasmState::WriteDisasmLine(JsonWriter &json, const DisassemblyLi else json.writeString("symbol", addressSymbol); + const u32 funcAddress = g_symbolMap->GetFunctionStart(addr); + const std::string funcName = g_symbolMap->GetLabelString(funcAddress); + if (funcName.empty()) + json.writeNull("function"); + else + json.writeString("function", funcName); + + if (l.type == DISTYPE_DATA) { + u32 dataStart = g_symbolMap->GetDataStart(addr); + if (dataStart == -1) + dataStart = addr; + const std::string dataLabel = g_symbolMap->GetLabelString(dataStart); + + json.pushDict("dataSymbol"); + json.writeUint("start", dataStart); + if (dataLabel.empty()) + json.writeNull("label"); + else + json.writeString("label", dataLabel); + json.pop(); + } else { + json.writeNull("dataSymbol"); + } + bool enabled; // TODO: Account for bp inside macro? if (CBreakPoints::IsAddressBreakPoint(addr, &enabled)) { From 085bcde86580b0fa965ca0ba288f153f15a19ef3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 May 2018 12:51:04 -0700 Subject: [PATCH 206/301] Debugger: Ignore invalid branches. These happen on bytes that are not actually code. --- Core/Debugger/DisassemblyManager.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index c520f86f5f..b04c40cc16 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -491,11 +491,12 @@ void DisassemblyFunction::generateBranchLines() MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos); bool inFunction = (opInfo.branchTarget >= address && opInfo.branchTarget < end); - if (opInfo.isBranch && !opInfo.isBranchToRegister && !opInfo.isLinkedBranch && inFunction) - { + if (opInfo.isBranch && !opInfo.isBranchToRegister && !opInfo.isLinkedBranch && inFunction) { + if (!Memory::IsValidAddress(opInfo.branchTarget)) + continue; + BranchLine line; - if (opInfo.branchTarget < funcPos) - { + if (opInfo.branchTarget < funcPos) { line.first = opInfo.branchTarget; line.second = funcPos; line.type = LINE_UP; @@ -756,13 +757,14 @@ void DisassemblyOpcode::getBranchLines(u32 start, u32 size, std::vector Date: Sat, 9 Jun 2018 10:35:40 -0700 Subject: [PATCH 207/301] Qt: Remove bundled assets reader. It was no longer being used or linked, so it just meant the assets were missing. With this removed, the assets (lang inis, shaders, etc.) are available again. Fixes #9831 - likely broken since #9061, which stopped using the qrc. --- CMakeLists.txt | 2 +- Qt/desktop_assets.qrc | 16 ---------- UI/NativeApp.cpp | 4 +-- ext/native/file/zip_read.cpp | 57 ------------------------------------ ext/native/file/zip_read.h | 15 ---------- 5 files changed, 2 insertions(+), 92 deletions(-) delete mode 100644 Qt/desktop_assets.qrc diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e0db28925..138661ef83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -763,7 +763,7 @@ elseif(USING_QT_UI) Qt/mainwindow.h ) add_definitions(-DUSING_QT_UI) - include_directories(${CMAKE_CURRENT_BINARY_DIR} Qt Qt/Debugger) + include_directories(${CMAKE_CURRENT_BINARY_DIR} Qt) set(nativeExtraLibs ${nativeExtraLibs} Qt5::OpenGL Qt5::Gui Qt5::Core) set(TargetBin PPSSPPQt) diff --git a/Qt/desktop_assets.qrc b/Qt/desktop_assets.qrc deleted file mode 100644 index c65de91f71..0000000000 --- a/Qt/desktop_assets.qrc +++ /dev/null @@ -1,16 +0,0 @@ - - - ../assets/ui_atlas.zim - ../assets/ppge_atlas.zim - ../assets/lang - ../assets/compat.ini - ../assets/langregion.ini - ../assets/unknown.png - ../assets/rargray.png - ../assets/zip.png - ../assets/shaders - ../flash0/font - resources/breakpointDisable.ico - resources/breakpoint.ico - - diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 8ae0bedb41..73781e2b61 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -360,9 +360,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #endif // We want this to be FIRST. -#ifdef USING_QT_UI - VFSRegister("", new AssetsAssetReader()); -#elif defined(IOS) +#if defined(IOS) // Packed assets are included in app VFSRegister("", new DirectoryAssetReader(external_dir)); #endif diff --git a/ext/native/file/zip_read.cpp b/ext/native/file/zip_read.cpp index fa3a7c38f6..5069cfc223 100644 --- a/ext/native/file/zip_read.cpp +++ b/ext/native/file/zip_read.cpp @@ -3,11 +3,6 @@ #include #include -#ifdef USING_QT_UI -#include -#include -#endif - #ifdef __ANDROID__ #include #endif @@ -66,58 +61,6 @@ uint8_t *ReadLocalFile(const char *filename, size_t *size) { return contents; } -#ifdef USING_QT_UI -uint8_t *AssetsAssetReader::ReadAsset(const char *path, size_t *size) { - QFile asset(QString(":/assets/") + path); - if (!asset.open(QIODevice::ReadOnly)) - return 0; - - uint8_t *contents = new uint8_t[asset.size()+1]; - memcpy(contents, (uint8_t*)asset.readAll().data(), asset.size()); - contents[asset.size()] = 0; - *size = asset.size(); - asset.close(); - return contents; -} - -bool AssetsAssetReader::GetFileListing(const char *path, std::vector *listing, const char *filter = 0) -{ - QDir assetDir(QString(":/assets/") + path); - QStringList filters = QString(filter).split(':', QString::SkipEmptyParts); - for (int i = 0; i < filters.count(); i++) - filters[i].prepend("*."); - - QFileInfoList infoList = assetDir.entryInfoList(filters, QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name); - foreach(QFileInfo qinfo, infoList) { - FileInfo info; - info.name = qinfo.fileName().toStdString(); - info.fullName = qinfo.absoluteFilePath().remove(":/assets/").toStdString(); - info.exists = true; - info.isWritable = false; - info.isDirectory = qinfo.isDir(); - listing->push_back(info); - } - return true; -} - -bool AssetsAssetReader::GetFileInfo(const char *path, FileInfo *info) { - QFileInfo qinfo(QString(":/assets/") + path); - if (!qinfo.exists()) { - info->exists = false; - info->size = 0; - return false; - } - - info->fullName = path; - info->exists = true; - info->isWritable = false; - info->isDirectory = qinfo.isDir(); - info->size = qinfo.size(); - return true; -} - -#endif - #ifdef __ANDROID__ ZipAssetReader::ZipAssetReader(const char *zip_file, const char *in_zip_path) { diff --git a/ext/native/file/zip_read.h b/ext/native/file/zip_read.h index 7e2ecbbf45..3607ddb926 100644 --- a/ext/native/file/zip_read.h +++ b/ext/native/file/zip_read.h @@ -26,21 +26,6 @@ public: virtual std::string toString() const = 0; }; -#ifdef USING_QT_UI -class AssetsAssetReader : public AssetReader { -public: - AssetsAssetReader() {} - ~AssetsAssetReader() {} - // use delete[] - virtual uint8_t *ReadAsset(const char *path, size_t *size); - virtual bool GetFileListing(const char *path, std::vector *listing, const char *filter); - virtual bool GetFileInfo(const char *path, FileInfo *info); - virtual std::string toString() const { - return ":assets/"; - } -}; -#endif - #ifdef __ANDROID__ uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size); class ZipAssetReader : public AssetReader { From cc1fa20afb03b70f654b7d30c925a798a28820a6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 11:34:12 -0700 Subject: [PATCH 208/301] Qt: Use Roboto Condensed like on other platforms. May improve font fallback issues in #7581. --- Core/Config.h | 4 +++- UI/NativeApp.cpp | 25 ++++++++++++++++++++++--- ext/native/gfx_es2/draw_text_qt.cpp | 5 ++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Core/Config.h b/Core/Config.h index 05a4824203..6778413cf2 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -106,10 +106,12 @@ public: #if defined(USING_WIN_UI) bool bPauseOnLostFocus; bool bTopMost; - std::string sFont; bool bIgnoreWindowsKey; bool bRestartRequired; #endif +#if defined(USING_WIN_UI) || defined(USING_QT_UI) + std::string sFont; +#endif bool bPauseWhenMinimized; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 73781e2b61..a45c6c7597 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -105,6 +105,9 @@ #if !defined(MOBILE_DEVICE) && defined(USING_QT_UI) #include "Qt/QtHost.h" #endif +#if defined(USING_QT_UI) +#include +#endif // The new UI framework, for initialization @@ -276,7 +279,7 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo #endif } -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#if defined(USING_WIN_UI) && !PPSSPP_PLATFORM(UWP) static bool CheckFontIsUsable(const wchar_t *fontFace) { wchar_t actualFontFace[1024] = { 0 }; @@ -558,7 +561,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Note to translators: do not translate this/add this to PPSSPP-lang's files. // It's intended to be custom for every user. // Only add it to your own personal copies of PPSSPP. -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#if defined(USING_WIN_UI) && !PPSSPP_PLATFORM(UWP) // TODO: Could allow a setting to specify a font file to load? // TODO: Make this a constant if we can sanely load the font on other systems? AddFontResourceEx(L"assets/Roboto-Condensed.ttf", FR_PRIVATE, NULL); @@ -568,6 +571,22 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } else { g_Config.sFont = des->T("Font", "Roboto"); } +#elif defined(USING_QT_UI) + size_t fontSize = 0; + uint8_t *fontData = VFSReadFile("Roboto-Condensed.ttf", &fontSize); + if (fontData) { + int fontID = QFontDatabase::addApplicationFontFromData(QByteArray((const char *)fontData, fontSize)); + delete [] fontData; + + QStringList fontsFound = QFontDatabase::applicationFontFamilies(fontID); + if (fontsFound.size() >= 1) { + // Might be "Roboto" or "Roboto Condensed". + g_Config.sFont = des->T("Font", fontsFound.at(0).toUtf8().constData()); + } + } else { + // Let's try for it being a system font. + g_Config.sFont = des->T("Font", "Roboto Condensed"); + } #endif if (!boot_filename.empty() && stateToLoad != NULL) { @@ -618,7 +637,7 @@ static UI::Style MakeStyle(uint32_t fg, uint32_t bg) { } static void UIThemeInit() { -#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#if (defined(USING_WIN_UI) && !PPSSPP_PLATFORM(UWP)) || defined(USING_QT_UI) ui_theme.uiFont = UI::FontStyle(UBUNTU24, g_Config.sFont.c_str(), 22); ui_theme.uiFontSmall = UI::FontStyle(UBUNTU24, g_Config.sFont.c_str(), 15); ui_theme.uiFontSmaller = UI::FontStyle(UBUNTU24, g_Config.sFont.c_str(), 12); diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index 973e8d27ad..a2fae32f39 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -23,8 +23,7 @@ TextDrawerQt::~TextDrawerQt() { } uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) { - // We will only use the default font - uint32_t fontHash = 0; //hash::Adler32((const uint8_t *)fontName, strlen(fontName)); + uint32_t fontHash = fontName && strlen(fontName) ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0; fontHash ^= size; fontHash ^= flags << 10; @@ -34,7 +33,7 @@ uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) { return fontHash; } - QFont* font = new QFont(); + QFont* font = fontName ? new QFont(fontName) : new QFont(); font->setPixelSize(size + 6); fontMap_[fontHash] = font; fontHash_ = fontHash; From 82b6cda5ef3b9fa63bef47cfb63efc00b37b9550 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 16:21:28 -0700 Subject: [PATCH 209/301] Qt: Return correct refresh rate. Although, should probably update it if the screen changes, meh. --- Qt/QtMain.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 395af8c5bb..197eed4984 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "ext/glslang/glslang/Public/ShaderLang.h" @@ -34,6 +35,7 @@ #include MainUI *emugl = NULL; +static int refreshRate = 60000; #ifdef SDL extern void mixaudio(void *userdata, Uint8 *stream, int len) { @@ -67,7 +69,7 @@ int System_GetPropertyInt(SystemProperty prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return 44100; case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; + return refreshRate; case SYSPROP_DEVICE_TYPE: #if defined(__ANDROID__) return DEVICE_TYPE_MOBILE; @@ -514,6 +516,9 @@ int main(int argc, char *argv[]) g_dpi_scale_real_y = g_dpi_scale_y; dp_xres = (int)(pixel_xres * g_dpi_scale_x); dp_yres = (int)(pixel_yres * g_dpi_scale_y); + + refreshRate = (int)(a.primaryScreen()->refreshRate() * 1000); + std::string savegame_dir = "."; std::string external_dir = "."; #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) From a448ef536b49c251f8d60cd1a30ec0c0f926c543 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 16:28:15 -0700 Subject: [PATCH 210/301] Qt: Toggle fullscreen like on all other platforms. --- Qt/QtMain.cpp | 7 +---- Qt/QtMain.h | 3 +- Qt/mainwindow.cpp | 58 ++++++++++++++++++------------------- Qt/mainwindow.h | 3 +- UI/NativeApp.cpp | 6 ++-- Windows/main.cpp | 3 -- ext/native/base/NativeApp.h | 2 +- 7 files changed, 38 insertions(+), 44 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 197eed4984..304d164ab0 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -528,12 +528,7 @@ int main(int argc, char *argv[]) savegame_dir += "/"; external_dir += "/"; - bool fullscreenCLI=false; - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i],"--fullscreen")) - fullscreenCLI=true; - } - NativeInit(argc, (const char **)argv, savegame_dir.c_str(), external_dir.c_str(), nullptr, fullscreenCLI); + NativeInit(argc, (const char **)argv, savegame_dir.c_str(), external_dir.c_str(), nullptr); // TODO: Support other backends than GL, like Vulkan, in the Qt backend. g_Config.iGPUBackend = (int)GPUBackend::OPENGL; diff --git a/Qt/QtMain.h b/Qt/QtMain.h index 7f67794487..958e139bcc 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -110,6 +110,8 @@ public: explicit MainUI(QWidget *parent = 0); ~MainUI(); + void resizeGL(int w, int h); + public slots: QString InputBoxGetQString(QString title, QString defaultValue); @@ -123,7 +125,6 @@ protected: bool event(QEvent *e); void initializeGL(); - void resizeGL(int w, int h); void paintGL(); void updateAccelerometer(); diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 904edc6095..9d28dac208 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -36,10 +36,7 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : createMenus(); updateMenus(); - SetWindowScale(-1); - - if(fullscreen) - fullscrAct(); + SetFullScreen(fullscreen); QObject::connect(emugl, SIGNAL(doubleClick()), this, SLOT(fullscrAct())); QObject::connect(emugl, SIGNAL(newFrame()), this, SLOT(newFrame())); @@ -99,9 +96,6 @@ void MainWindow::updateMenus() foreach(QAction * action, displayLayoutGroup->actions()) { if (g_Config.iSmallDisplayZoomType == action->data().toInt()) { - - NativeMessageReceived("gpu_resized", ""); - action->setChecked(true); break; } @@ -135,8 +129,8 @@ void MainWindow::updateMenus() void MainWindow::bootDone() { - if(g_Config.bFullScreen != isFullScreen()) - fullscrAct(); + if (g_Config.bFullScreen != isFullScreen()) + SetFullScreen(g_Config.bFullScreen); if (nextState == CORE_RUNNING) runAct(); @@ -300,40 +294,43 @@ void MainWindow::raiseTopMost() { setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); raise(); - activateWindow(); - + activateWindow(); } -void MainWindow::fullscrAct() -{ - if(isFullScreen()) { - g_Config.bFullScreen = false; +void MainWindow::SetFullScreen(bool fullscreen) { + if (fullscreen) { + menuBar()->hide(); + + emugl->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + emugl->resizeGL(emugl->size().width(), emugl->size().height()); + // TODO: Won't showFullScreen do this for us? + setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + + showFullScreen(); + InitPadLayout(dp_xres, dp_yres); + + if (GetUIState() == UISTATE_INGAME && !g_Config.bShowTouchControls) + QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + } else { menuBar()->show(); updateMenus(); showNormal(); SetWindowScale(-1); InitPadLayout(dp_xres, dp_yres); + if (GetUIState() == UISTATE_INGAME && QApplication::overrideCursor()) QApplication::restoreOverrideCursor(); } - else { - g_Config.bFullScreen = true; - menuBar()->hide(); +} - emugl->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); +void MainWindow::fullscrAct() +{ + // Toggle the current state. + g_Config.bFullScreen = !isFullScreen(); + SetFullScreen(g_Config.bFullScreen); - showFullScreen(); - - NativeMessageReceived("gpu_resized", ""); - InitPadLayout(dp_xres, dp_yres); - if (GetUIState() == UISTATE_INGAME && !g_Config.bShowTouchControls) - QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); - - } - QTimer::singleShot(1000, this, SLOT(raiseTopMost())); } @@ -396,6 +393,7 @@ void MainWindow::SetWindowScale(int zoom) { g_Config.iWindowHeight = height; emugl->setFixedSize(g_Config.iWindowWidth, g_Config.iWindowHeight); + emugl->resizeGL(g_Config.iWindowWidth, g_Config.iWindowHeight); setFixedSize(sizeHint()); } diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index 57d24acce1..516d07bcdf 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -30,7 +30,7 @@ class MainWindow : public QMainWindow Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0, bool fullscreen=false); + explicit MainWindow(QWidget *parent = nullptr, bool fullscreen = false); ~MainWindow() { }; CoreState GetNextState() { return nextState; } @@ -144,6 +144,7 @@ private: void bootDone(); void SetWindowScale(int zoom); void SetGameTitle(QString text); + void SetFullScreen(bool fullscreen); void loadLanguage(const QString &language, bool retranslate); void createMenus(); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 97b42ffca1..5f2ca00c09 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -345,7 +345,7 @@ void CreateDirectoriesAndroid() { File::CreateEmptyFile(g_Config.memStickDirectory + "PSP/SAVEDATA/.nomedia"); } -void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir, bool fs) { +void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir) { net::Init(); // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times. InitFastMath(cpu_info.bNEON); @@ -474,6 +474,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #endif if (!strncmp(argv[i], "--pause-menu-exit", strlen("--pause-menu-exit"))) g_Config.bPauseMenuExitsEmulator = true; + if (!strcmp(argv[i], "--fullscreen")) + g_Config.bFullScreen = true; break; } } else { @@ -615,7 +617,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch isOuya = KeyMap::IsOuya(sysName); #if !defined(MOBILE_DEVICE) && defined(USING_QT_UI) - MainWindow* mainWindow = new MainWindow(0,fs); + MainWindow *mainWindow = new MainWindow(nullptr, g_Config.bFullScreen); mainWindow->show(); if (host == nullptr) { host = new QtHost(mainWindow); diff --git a/Windows/main.cpp b/Windows/main.cpp index 8d81eae7fa..a003e3739a 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -458,9 +458,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin break; } - if (wideArgs[i] == L"--fullscreen") - g_Config.bFullScreen = true; - if (wideArgs[i] == L"--windowed") g_Config.bFullScreen = false; diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index 36ef2d3d8b..39638436ab 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -45,7 +45,7 @@ bool NativeIsAtTopLevel(); // The very first function to be called after NativeGetAppInfo. Even NativeMix is not called // before this, although it may be called at any point in time afterwards (on any thread!) // This functions must NOT call OpenGL. Main thread. -void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir, bool fs=false); +void NativeInit(int argc, const char *argv[], const char *savegame_dir, const char *external_dir, const char *cache_dir); // Runs after NativeInit() at some point. May (and probably should) call OpenGL. // Should not initialize anything screen-size-dependent - do that in NativeResized. From b3c21517b05c8cf04ddd635503fa09d48f8f1712 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 16:35:13 -0700 Subject: [PATCH 211/301] Qt: Allow for larger screen sizes. --- Qt/mainwindow.cpp | 8 ++++---- Qt/mainwindow.h | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 9d28dac208..36db6361ff 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -382,8 +382,8 @@ void MainWindow::SetWindowScale(int zoom) { // Update to the specified factor. Let's clamp first. if (zoom < 1) zoom = 1; - if (zoom > 4) - zoom = 4; + if (zoom > 10) + zoom = 10; width = (g_Config.IsPortrait() ? 272 : 480) * zoom; height = (g_Config.IsPortrait() ? 480 : 272) * zoom; @@ -496,8 +496,8 @@ void MainWindow::createMenus() // - Screen Size MenuTree* screenMenu = new MenuTree(this, videoMenu, QT_TR_NOOP("&Screen Size")); screenGroup = new MenuActionGroup(this, screenMenu, SLOT(screenGroup_triggered(QAction *)), - QStringList() << "1x" << "2x" << "3x" << "4x", - QList() << 1 << 2 << 3 << 4, + QStringList() << "1x" << "2x" << "3x" << "4x" << "5x" << "6x" << "7x" << "8x" << "9x" << "10x", + QList() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10, QList() << Qt::CTRL + Qt::Key_1 << Qt::CTRL + Qt::Key_2 << Qt::CTRL + Qt::Key_3 << Qt::CTRL + Qt::Key_4); MenuTree* displayLayoutMenu = new MenuTree(this, videoMenu, QT_TR_NOOP("&Display Layout Options")); diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index 516d07bcdf..90ab051b91 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -99,12 +99,18 @@ private slots: // Video void anisotropicGroup_triggered(QAction *action) { g_Config.iAnisotropyLevel = action->data().toInt(); } - void bufferRenderAct() { g_Config.iRenderingMode = !g_Config.iRenderingMode; } + void bufferRenderAct() { + g_Config.iRenderingMode = !g_Config.iRenderingMode; + NativeMessageReceived("gpu_resized", ""); + } void linearAct() { g_Config.iTexFiltering = (g_Config.iTexFiltering != 0) ? 0 : 3; } void screenGroup_triggered(QAction *action) { SetWindowScale(action->data().toInt()); } - void displayLayoutGroup_triggered(QAction *action) { g_Config.iSmallDisplayZoomType = action->data().toInt(); } + void displayLayoutGroup_triggered(QAction *action) { + g_Config.iSmallDisplayZoomType = action->data().toInt(); + NativeMessageReceived("gpu_resized", ""); + } void transformAct() { g_Config.bHardwareTransform = !g_Config.bHardwareTransform; } void vertexCacheAct() { g_Config.bVertexCache = !g_Config.bVertexCache; } void frameskipAct() { g_Config.iFrameSkip = !g_Config.iFrameSkip; } @@ -114,7 +120,10 @@ private slots: void fullscrAct(); void raiseTopMost(); - void statsAct() { g_Config.bShowDebugStats = !g_Config.bShowDebugStats; } + void statsAct() { + g_Config.bShowDebugStats = !g_Config.bShowDebugStats; + NativeMessageReceived("clear jit", ""); + } void showFPSAct() { g_Config.iShowFPSCounter = !g_Config.iShowFPSCounter; } // Logs From 34cef82ea33e937e3d3564dee32618e199acc72a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 16:50:31 -0700 Subject: [PATCH 212/301] SDL: Use UpdateScreenScale(). --- SDL/SDLMain.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 081d924009..25f23e37a2 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -602,11 +602,9 @@ int main(int argc, char *argv[]) { Uint32 window_flags = SDL_GetWindowFlags(window); bool fullscreen = (window_flags & SDL_WINDOW_FULLSCREEN); - pixel_xres = event.window.data1; - pixel_yres = event.window.data2; - dp_xres = (float)pixel_xres * dpi_scale; - dp_yres = (float)pixel_yres * dpi_scale; - NativeResized(); + if (UpdateScreenScale(event.window.data1, event.window.data2)) { + NativeMessageReceived("gpu_resized", ""); + } // Set variable here in case fullscreen was toggled by hotkey g_Config.bFullScreen = fullscreen; @@ -760,7 +758,7 @@ int main(int argc, char *argv[]) { lastUIState = GetUIState(); if (lastUIState == UISTATE_INGAME && g_Config.bFullScreen && !g_Config.bShowTouchControls) SDL_ShowCursor(SDL_DISABLE); - if (lastUIState != UISTATE_INGAME && g_Config.bFullScreen) + if (lastUIState != UISTATE_INGAME || !g_Config.bFullScreen) SDL_ShowCursor(SDL_ENABLE); } #endif From 9be20323efb16cf714256d4e88249156290aa73c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 16:57:00 -0700 Subject: [PATCH 213/301] SDL: Start with window hidden. This fixes start in fullscreen on Mac. Otherwise, it animates to fullscreen and back for every GL init attempt. --- SDL/SDLGLGraphicsContext.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index b474da6497..a225831a60 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -158,7 +158,9 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std: #endif }; - mode |= SDL_WINDOW_OPENGL; + // We start hidden because we have to try several windows. + // On Mac, full screen animates so each attempt is slow. + mode |= SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN; SDL_GLContext glContext = nullptr; for (size_t i = 0; i < ARRAY_SIZE(attemptVersions); ++i) { @@ -217,6 +219,9 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std: } } + // At this point, we have a window that we can show finally. + SDL_ShowWindow(window); + #ifdef USING_EGL EGL_Init(); #endif From 4232ef59b4b81a9f5e87f8c90477bac7e85a8cd6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 18:26:09 -0700 Subject: [PATCH 214/301] Debugger: Fix bounds warnings in debug mode. Technically these were accessing outside the container. --- Windows/GEDebugger/TabState.cpp | 2 +- ext/native/net/websocket_server.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 20ea2cdc3a..494cf3e999 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -880,7 +880,7 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) { SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE); // Ehh, kinda ugly. - if (rows_ == &watchList[0]) { + if (!watchList.empty() && rows_ == &watchList[0]) { ModifyMenu(subMenu, ID_GEDBG_WATCH, MF_BYCOMMAND | MF_STRING, ID_GEDBG_WATCH, L"Remove Watch"); } else { ModifyMenu(subMenu, ID_GEDBG_WATCH, MF_BYCOMMAND | MF_STRING, ID_GEDBG_WATCH, L"Add Watch"); diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index 40cadfb733..a5fcb81107 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -526,7 +526,7 @@ void WebSocketServer::SendFlush() { // Drain out as much of our buffer as possible. size_t totalPushed = 0; - while (!outBuf_.empty()) { + while (outBuf_.size() - totalPushed != 0) { size_t pushed = out_->PushAtMost((const char *)&outBuf_[totalPushed], outBuf_.size() - totalPushed); if (pushed == 0) break; From cd85dec01ba0549352c34a085cdec091a9258780 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 18:26:41 -0700 Subject: [PATCH 215/301] Debugger: Prevent infinite loop on dead socket. If send() fails, abort instead of retrying forever. --- ext/native/net/sinks.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/native/net/sinks.cpp b/ext/native/net/sinks.cpp index d8d47dd080..b3ad60fcc9 100644 --- a/ext/native/net/sinks.cpp +++ b/ext/native/net/sinks.cpp @@ -333,6 +333,8 @@ bool OutputSink::Flush(bool allowBlock) { if (!allowBlock || !Block()) { return false; } + } else if (bytes < 0) { + return false; } } From 1cfb21931d0d478ff0f861fe1bca612b8e5bf1cf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 18:27:29 -0700 Subject: [PATCH 216/301] Vulkan: Properly transition stencil/depth for read. Need to transition the entire texture, so both aspects must be set. --- ext/native/thin3d/VulkanQueueRunner.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index ed71016192..7b6d54b0cf 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1086,7 +1086,12 @@ void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemor } barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; + if (img.format == VK_FORMAT_D16_UNORM_S8_UINT || img.format == VK_FORMAT_D24_UNORM_S8_UINT || img.format == VK_FORMAT_D32_SFLOAT_S8_UINT) { + // Barrier must specify both for combined depth/stencil buffers. + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + } else { + barrier.subresourceRange.aspectMask = aspect; + } barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; @@ -1140,7 +1145,12 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemor } barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.subresourceRange.aspectMask = aspect; + if (img.format == VK_FORMAT_D16_UNORM_S8_UINT || img.format == VK_FORMAT_D24_UNORM_S8_UINT || img.format == VK_FORMAT_D32_SFLOAT_S8_UINT) { + // Barrier must specify both for combined depth/stencil buffers. + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + } else { + barrier.subresourceRange.aspectMask = aspect; + } barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; img.layout = barrier.newLayout; @@ -1192,11 +1202,9 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd VKRImage *srcImage; if (step.readback.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { srcImage = &step.readback.src->color; - } - else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { + } else if (step.readback.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { srcImage = &step.readback.src->depth; - } - else { + } else { _dbg_assert_msg_(G3D, false, "No image aspect to readback?"); return; } From 0ed2c0335049522ed64587dcb5db0875ccea8ecd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Jun 2018 14:54:42 -0700 Subject: [PATCH 217/301] GE Debugger: Prevent hang on shutdown. Since we're blocking the Emu thread, we can't use a hook from the Emu thread to wake up. The change to lifecycle callbacks caused this. --- Core/Core.cpp | 16 ++++++++++++---- Core/Core.h | 5 +++++ GPU/Debugger/Stepping.cpp | 10 ++++------ GPU/Debugger/Stepping.h | 2 +- Windows/GEDebugger/GEDebugger.cpp | 2 +- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 5e32d29af4..bb02a1c08d 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -54,7 +54,8 @@ static std::condition_variable m_InactiveCond; static std::mutex m_hInactiveMutex; static bool singleStepPending = false; static int steppingCounter = 0; -static std::set shutdownFuncs; +static std::set lifecycleFuncs; +static std::set stopFuncs; static bool windowHidden = false; static double lastActivity = 0.0; static double lastKeepAwake = 0.0; @@ -76,17 +77,24 @@ void Core_NotifyActivity() { } void Core_ListenLifecycle(CoreLifecycleFunc func) { - shutdownFuncs.insert(func); + lifecycleFuncs.insert(func); } void Core_NotifyLifecycle(CoreLifecycle stage) { - for (auto it = shutdownFuncs.begin(); it != shutdownFuncs.end(); ++it) { - (*it)(stage); + for (auto func : lifecycleFuncs) { + func(stage); } } +void Core_ListenStopRequest(CoreStopRequestFunc func) { + stopFuncs.insert(func); +} + void Core_Stop() { Core_UpdateState(CORE_POWERDOWN); + for (auto func : stopFuncs) { + func(); + } } bool Core_IsStepping() { diff --git a/Core/Core.h b/Core/Core.h index 0b87f2b393..69749f0d70 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -51,10 +51,15 @@ enum class CoreLifecycle { MEMORY_REINITED, }; +// Callback is called on the Emu thread. typedef void (* CoreLifecycleFunc)(CoreLifecycle stage); void Core_ListenLifecycle(CoreLifecycleFunc func); void Core_NotifyLifecycle(CoreLifecycle stage); +// Callback is executed on requesting thread. +typedef void (* CoreStopRequestFunc)(); +void Core_ListenStopRequest(CoreStopRequestFunc callback); + bool Core_IsStepping(); bool Core_IsActive(); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index ea93778cff..c765d15b76 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -197,12 +197,10 @@ void ResumeFromStepping() { SetPauseAction(PAUSE_CONTINUE, false); } -void ForceUnpause(CoreLifecycle stage) { - if (stage == CoreLifecycle::STOPPING) { - SetPauseAction(PAUSE_CONTINUE, false); - actionComplete = true; - actionWait.notify_all(); - } +void ForceUnpause() { + SetPauseAction(PAUSE_CONTINUE, false); + actionComplete = true; + actionWait.notify_all(); } } // namespace diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index 3e9b5086cc..d99e8af904 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -38,5 +38,5 @@ namespace GPUStepping { bool GPU_SetCmdValue(u32 op); void ResumeFromStepping(); - void ForceUnpause(CoreLifecycle stage); + void ForceUnpause(); }; diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index e77238ba3b..ad68557f64 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -67,7 +67,7 @@ void CGEDebugger::Init() { CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent) { GPUBreakpoints::Init(); - Core_ListenLifecycle(ForceUnpause); + Core_ListenStopRequest(ForceUnpause); // minimum size = a little more than the default RECT windowRect; From ccef997a7a1d5126f592d091cc7381f6c0041e09 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Jun 2018 14:56:25 -0700 Subject: [PATCH 218/301] GE Debugger: Fix asserts when reading debug tex. This is unfortunate, but right now BuildTexture() applies the sampling settings which happen within a render pass. So we must have a render pass. --- GPU/GLES/TextureCacheGLES.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 8f6368c66b..e390ed322a 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -841,6 +841,8 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) // Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer. TexCacheEntry *entry = nextTexture_; + // We might need a render pass to set the sampling params, unfortunately. Otherwise BuildTexture may crash. + framebufferManagerGL_->RebindFramebuffer(); ApplyTexture(); // TODO: Centralize? From f7443aaa156c95d9db673892fc7dcb68b188ca5f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Jun 2018 15:06:40 -0700 Subject: [PATCH 219/301] GE Debugger: Use a class for dump execution. --- GPU/Debugger/Record.cpp | 97 +++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index ba98667954..1d42d937b1 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -86,12 +86,35 @@ static std::vector lastRegisters; static std::vector lastTextures; // TODO: Maybe move execute to another file? -static u32 execMemcpyDest; -static u32 execListBuf; -static u32 execListPos; -static u32 execListID; -static const int LIST_BUF_SIZE = 256 * 1024; -static std::vector execListQueue; +class DumpExecute { +public: + ~DumpExecute(); + + bool Run(); + +private: + bool SubmitCmds(void *p, u32 sz); + void SubmitListEnd(); + + void Init(u32 ptr, u32 sz); + void Registers(u32 ptr, u32 sz); + void Vertices(u32 ptr, u32 sz); + void Indices(u32 ptr, u32 sz); + void Clut(u32 ptr, u32 sz); + void TransferSrc(u32 ptr, u32 sz); + void Memset(u32 ptr, u32 sz); + void MemcpyDest(u32 ptr, u32 sz); + void Memcpy(u32 ptr, u32 sz); + void Texture(int level, u32 ptr, u32 sz); + void Display(u32 ptr, u32 sz); + + u32 execMemcpyDest = 0; + u32 execListBuf = 0; + u32 execListPos = 0; + u32 execListID = 0; + const int LIST_BUF_SIZE = 256 * 1024; + std::vector execListQueue; +}; // This class maps pushbuffer (dump data) sections to PSP memory. // Dumps can be larger than available PSP memory, because they include generated data too. @@ -731,7 +754,7 @@ void NotifyFrame() { } } -static bool ExecuteSubmitCmds(void *p, u32 sz) { +bool DumpExecute::SubmitCmds(void *p, u32 sz) { if (execListBuf == 0) { u32 allocSize = LIST_BUF_SIZE; execListBuf = userMemory.Alloc(allocSize, "List buf"); @@ -781,7 +804,7 @@ static bool ExecuteSubmitCmds(void *p, u32 sz) { return true; } -static void ExecuteSubmitListEnd() { +void DumpExecute::SubmitListEnd() { if (execListPos == 0) { return; } @@ -800,16 +823,16 @@ static void ExecuteSubmitListEnd() { CoreTiming::ForceCheck(); } -static void ExecuteInit(u32 ptr, u32 sz) { +void DumpExecute::Init(u32 ptr, u32 sz) { gstate.Restore((u32_le *)(pushbuf.data() + ptr)); gpu->ReapplyGfxState(); } -static void ExecuteRegisters(u32 ptr, u32 sz) { - ExecuteSubmitCmds(pushbuf.data() + ptr, sz); +void DumpExecute::Registers(u32 ptr, u32 sz) { + SubmitCmds(pushbuf.data() + ptr, sz); } -static void ExecuteVertices(u32 ptr, u32 sz) { +void DumpExecute::Vertices(u32 ptr, u32 sz) { u32 psp = execMapping.Map(ptr, sz); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for vertices"); @@ -820,7 +843,7 @@ static void ExecuteVertices(u32 ptr, u32 sz) { execListQueue.push_back((GE_CMD_VADDR << 24) | (psp & 0x00FFFFFF)); } -static void ExecuteIndices(u32 ptr, u32 sz) { +void DumpExecute::Indices(u32 ptr, u32 sz) { u32 psp = execMapping.Map(ptr, sz); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for indices"); @@ -831,7 +854,7 @@ static void ExecuteIndices(u32 ptr, u32 sz) { execListQueue.push_back((GE_CMD_IADDR << 24) | (psp & 0x00FFFFFF)); } -static void ExecuteClut(u32 ptr, u32 sz) { +void DumpExecute::Clut(u32 ptr, u32 sz) { u32 psp = execMapping.Map(ptr, sz); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for clut"); @@ -842,7 +865,7 @@ static void ExecuteClut(u32 ptr, u32 sz) { execListQueue.push_back((GE_CMD_CLUTADDR << 24) | (psp & 0x00FFFFFF)); } -static void ExecuteTransferSrc(u32 ptr, u32 sz) { +void DumpExecute::TransferSrc(u32 ptr, u32 sz) { u32 psp = execMapping.Map(ptr, sz); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for transfer"); @@ -853,7 +876,7 @@ static void ExecuteTransferSrc(u32 ptr, u32 sz) { execListQueue.push_back(((GE_CMD_TRANSFERSRC) << 24) | (psp & 0x00FFFFFF)); } -static void ExecuteMemset(u32 ptr, u32 sz) { +void DumpExecute::Memset(u32 ptr, u32 sz) { struct MemsetCommand { u32 dest; int value; @@ -867,18 +890,18 @@ static void ExecuteMemset(u32 ptr, u32 sz) { } } -static void ExecuteMemcpyDest(u32 ptr, u32 sz) { +void DumpExecute::MemcpyDest(u32 ptr, u32 sz) { execMemcpyDest = *(const u32 *)(pushbuf.data() + ptr); } -static void ExecuteMemcpy(u32 ptr, u32 sz) { +void DumpExecute::Memcpy(u32 ptr, u32 sz) { if (Memory::IsVRAMAddress(execMemcpyDest)) { Memory::MemcpyUnchecked(execMemcpyDest, pushbuf.data() + ptr, sz); gpu->PerformMemoryUpload(execMemcpyDest, sz); } } -static void ExecuteTexture(int level, u32 ptr, u32 sz) { +void DumpExecute::Texture(int level, u32 ptr, u32 sz) { u32 psp = execMapping.Map(ptr, sz); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for texture"); @@ -889,7 +912,7 @@ static void ExecuteTexture(int level, u32 ptr, u32 sz) { execListQueue.push_back(((GE_CMD_TEXADDR0 + level) << 24) | (psp & 0x00FFFFFF)); } -static void ExecuteDisplay(u32 ptr, u32 sz) { +void DumpExecute::Display(u32 ptr, u32 sz) { struct DisplayBufData { PSPPointer topaddr; u32 linesize, pixelFormat; @@ -901,7 +924,7 @@ static void ExecuteDisplay(u32 ptr, u32 sz) { __DisplaySetFramebuf(disp->topaddr.ptr, disp->linesize, disp->pixelFormat, 0); } -static void ExecuteFree() { +DumpExecute::~DumpExecute() { execMemcpyDest = 0; if (execListBuf) { userMemory.Free(execListBuf); @@ -914,43 +937,43 @@ static void ExecuteFree() { pushbuf.clear(); } -static bool ExecuteCommands() { +bool DumpExecute::Run() { for (const Command &cmd : commands) { switch (cmd.type) { case CommandType::INIT: - ExecuteInit(cmd.ptr, cmd.sz); + Init(cmd.ptr, cmd.sz); break; case CommandType::REGISTERS: - ExecuteRegisters(cmd.ptr, cmd.sz); + Registers(cmd.ptr, cmd.sz); break; case CommandType::VERTICES: - ExecuteVertices(cmd.ptr, cmd.sz); + Vertices(cmd.ptr, cmd.sz); break; case CommandType::INDICES: - ExecuteIndices(cmd.ptr, cmd.sz); + Indices(cmd.ptr, cmd.sz); break; case CommandType::CLUT: - ExecuteClut(cmd.ptr, cmd.sz); + Clut(cmd.ptr, cmd.sz); break; case CommandType::TRANSFERSRC: - ExecuteTransferSrc(cmd.ptr, cmd.sz); + TransferSrc(cmd.ptr, cmd.sz); break; case CommandType::MEMSET: - ExecuteMemset(cmd.ptr, cmd.sz); + Memset(cmd.ptr, cmd.sz); break; case CommandType::MEMCPYDEST: - ExecuteMemcpyDest(cmd.ptr, cmd.sz); + MemcpyDest(cmd.ptr, cmd.sz); break; case CommandType::MEMCPYDATA: - ExecuteMemcpy(cmd.ptr, cmd.sz); + Memcpy(cmd.ptr, cmd.sz); break; case CommandType::TEXTURE0: @@ -961,11 +984,11 @@ static bool ExecuteCommands() { case CommandType::TEXTURE5: case CommandType::TEXTURE6: case CommandType::TEXTURE7: - ExecuteTexture((int)cmd.type - (int)CommandType::TEXTURE0, cmd.ptr, cmd.sz); + Texture((int)cmd.type - (int)CommandType::TEXTURE0, cmd.ptr, cmd.sz); break; case CommandType::DISPLAY: - ExecuteDisplay(cmd.ptr, cmd.sz); + Display(cmd.ptr, cmd.sz); break; default: @@ -974,7 +997,7 @@ static bool ExecuteCommands() { } } - ExecuteSubmitListEnd(); + SubmitListEnd(); return true; } @@ -1028,13 +1051,11 @@ bool RunMountedReplay(const std::string &filename) { if (truncated) { ERROR_LOG(SYSTEM, "Truncated GE dump"); - ExecuteFree(); return false; } - bool success = ExecuteCommands(); - ExecuteFree(); - return success; + DumpExecute executor; + return executor.Run(); } }; From 582bc2d60ae80e6e8523776d99751890d363ed45 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 11 Jun 2018 15:08:27 -0700 Subject: [PATCH 220/301] GE Debugger: Stall less liberally on GE dump exec. This makes it easier to compare performance and flushing bugs. --- GPU/Debugger/Record.cpp | 101 +++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 1d42d937b1..fb130ef34a 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include "base/stringutil.h" @@ -93,6 +94,7 @@ public: bool Run(); private: + void SyncStall(); bool SubmitCmds(void *p, u32 sz); void SubmitListEnd(); @@ -114,6 +116,7 @@ private: u32 execListID = 0; const int LIST_BUF_SIZE = 256 * 1024; std::vector execListQueue; + u16 lastBufw_[8]{}; }; // This class maps pushbuffer (dump data) sections to PSP memory. @@ -125,7 +128,7 @@ private: class BufMapping { public: // Returns a pointer to contiguous memory for this access, or else 0 (failure). - u32 Map(u32 bufpos, u32 sz); + u32 Map(u32 bufpos, u32 sz, const std::function &flush); // Clear and reset allocations made. void Reset() { @@ -140,8 +143,8 @@ public: } protected: - u32 MapSlab(u32 bufpos); - u32 MapExtra(u32 bufpos, u32 sz); + u32 MapSlab(u32 bufpos, const std::function &flush); + u32 MapExtra(u32 bufpos, u32 sz, const std::function &flush); enum { // These numbers kept low because we only have 24 MB of user memory to map into. @@ -212,20 +215,20 @@ protected: static BufMapping execMapping; -u32 BufMapping::Map(u32 bufpos, u32 sz) { +u32 BufMapping::Map(u32 bufpos, u32 sz, const std::function &flush) { int slab1 = bufpos / SLAB_SIZE; int slab2 = (bufpos + sz - 1) / SLAB_SIZE; if (slab1 == slab2) { // Doesn't straddle, so we can just map to a slab. - return MapSlab(bufpos); + return MapSlab(bufpos, flush); } else { // We need contiguous, so we'll just allocate separately. - return MapExtra(bufpos, sz); + return MapExtra(bufpos, sz, flush); } } -u32 BufMapping::MapSlab(u32 bufpos) { +u32 BufMapping::MapSlab(u32 bufpos, const std::function &flush) { u32 slab_pos = (bufpos / SLAB_SIZE) * SLAB_SIZE; int best = 0; @@ -239,6 +242,9 @@ u32 BufMapping::MapSlab(u32 bufpos) { } } + // Stall before mapping a new slab. + flush(); + // Okay, we need to allocate. if (!slabs_[best].Setup(slab_pos)) { return 0; @@ -246,7 +252,7 @@ u32 BufMapping::MapSlab(u32 bufpos) { return slabs_[best].Ptr(bufpos); } -u32 BufMapping::MapExtra(u32 bufpos, u32 sz) { +u32 BufMapping::MapExtra(u32 bufpos, u32 sz, const std::function &flush) { for (int i = 0; i < EXTRA_COUNT; ++i) { // Might be likely to reuse larger buffers straddling slabs. if (extra_[i].Matches(bufpos, sz)) { @@ -254,6 +260,9 @@ u32 BufMapping::MapExtra(u32 bufpos, u32 sz) { } } + // Stall first, so we don't stomp existing RAM. + flush(); + int i = extraOffset_; extraOffset_ = (extraOffset_ + 1) % EXTRA_COUNT; @@ -754,6 +763,17 @@ void NotifyFrame() { } } +void DumpExecute::SyncStall() { + gpu->UpdateStall(execListID, execListPos); + s64 listTicks = gpu->GetListTicks(execListID); + if (listTicks != -1) { + currentMIPS->downcount -= listTicks - CoreTiming::GetTicks(); + } + + // Make sure downcount doesn't overflow. + CoreTiming::ForceCheck(); +} + bool DumpExecute::SubmitCmds(void *p, u32 sz) { if (execListBuf == 0) { u32 allocSize = LIST_BUF_SIZE; @@ -784,22 +804,42 @@ bool DumpExecute::SubmitCmds(void *p, u32 sz) { Memory::Write_U32((GE_CMD_JUMP << 24) | (execListBuf & 0x00FFFFFF), execListPos + 4); execListPos = execListBuf; + + // Don't continue until we've stalled. + SyncStall(); } Memory::MemcpyUnchecked(execListPos, execListQueue.data(), pendingSize); execListPos += pendingSize; + u32 writePos = execListPos; Memory::MemcpyUnchecked(execListPos, p, sz); execListPos += sz; - execListQueue.clear(); - gpu->UpdateStall(execListID, execListPos); - s64 listTicks = gpu->GetListTicks(execListID); - if (listTicks != -1) { - currentMIPS->downcount -= listTicks - CoreTiming::GetTicks(); + // TODO: Unfortunate. Maybe Texture commands should contain the bufw instead. + // The goal here is to realistically combine prims in dumps. Stalling for the bufw flushes. + u32_le *ops = (u32_le *)Memory::GetPointer(writePos); + for (u32 i = 0; i < sz / 4; ++i) { + u32 cmd = ops[i] >> 24; + if (cmd >= GE_CMD_TEXBUFWIDTH0 && cmd <= GE_CMD_TEXBUFWIDTH7) { + int level = cmd - GE_CMD_TEXBUFWIDTH0; + u16 bufw = ops[i] & 0xFFFF; + + // NOP the address part of the command to avoid a flush too. + if (bufw == lastBufw_[level]) + ops[i] = GE_CMD_NOP << 24; + else + ops[i] = (gstate.texbufwidth[level] & 0xFFFF0000) | bufw; + lastBufw_[level] = bufw; + } + + // Since we're here anyway, also NOP out texture addresses. + // This makes Step Tex not hit phantom textures. + if (cmd >= GE_CMD_TEXADDR0 && cmd <= GE_CMD_TEXADDR7) { + ops[i] = GE_CMD_NOP << 24; + } } - // Make sure downcount doesn't overflow. - CoreTiming::ForceCheck(); + execListQueue.clear(); return true; } @@ -814,13 +854,8 @@ void DumpExecute::SubmitListEnd() { Memory::Write_U32(GE_CMD_END << 24, execListPos + 4); execListPos += 8; - gpu->UpdateStall(execListID, execListPos); - currentMIPS->downcount -= gpu->GetListTicks(execListID) - CoreTiming::GetTicks(); - + SyncStall(); gpu->ListSync(execListID, 0); - - // Make sure downcount doesn't overflow. - CoreTiming::ForceCheck(); } void DumpExecute::Init(u32 ptr, u32 sz) { @@ -833,7 +868,7 @@ void DumpExecute::Registers(u32 ptr, u32 sz) { } void DumpExecute::Vertices(u32 ptr, u32 sz) { - u32 psp = execMapping.Map(ptr, sz); + u32 psp = execMapping.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for vertices"); return; @@ -844,7 +879,7 @@ void DumpExecute::Vertices(u32 ptr, u32 sz) { } void DumpExecute::Indices(u32 ptr, u32 sz) { - u32 psp = execMapping.Map(ptr, sz); + u32 psp = execMapping.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for indices"); return; @@ -855,7 +890,7 @@ void DumpExecute::Indices(u32 ptr, u32 sz) { } void DumpExecute::Clut(u32 ptr, u32 sz) { - u32 psp = execMapping.Map(ptr, sz); + u32 psp = execMapping.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for clut"); return; @@ -866,12 +901,15 @@ void DumpExecute::Clut(u32 ptr, u32 sz) { } void DumpExecute::TransferSrc(u32 ptr, u32 sz) { - u32 psp = execMapping.Map(ptr, sz); + u32 psp = execMapping.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for transfer"); return; } + // Need to sync in order to access gstate.transfersrcw. + SyncStall(); + execListQueue.push_back((gstate.transfersrcw & 0xFF00FFFF) | ((psp >> 8) & 0x00FF0000)); execListQueue.push_back(((GE_CMD_TRANSFERSRC) << 24) | (psp & 0x00FFFFFF)); } @@ -886,6 +924,7 @@ void DumpExecute::Memset(u32 ptr, u32 sz) { const MemsetCommand *data = (const MemsetCommand *)(pushbuf.data() + ptr); if (Memory::IsVRAMAddress(data->dest)) { + SyncStall(); gpu->PerformMemorySet(data->dest, (u8)data->value, data->sz); } } @@ -896,20 +935,23 @@ void DumpExecute::MemcpyDest(u32 ptr, u32 sz) { void DumpExecute::Memcpy(u32 ptr, u32 sz) { if (Memory::IsVRAMAddress(execMemcpyDest)) { + SyncStall(); Memory::MemcpyUnchecked(execMemcpyDest, pushbuf.data() + ptr, sz); gpu->PerformMemoryUpload(execMemcpyDest, sz); } } void DumpExecute::Texture(int level, u32 ptr, u32 sz) { - u32 psp = execMapping.Map(ptr, sz); + u32 psp = execMapping.Map(ptr, sz, std::bind(&DumpExecute::SyncStall, this)); if (psp == 0) { ERROR_LOG(SYSTEM, "Unable to allocate for texture"); return; } - execListQueue.push_back((gstate.texbufwidth[level] & 0xFF00FFFF) | ((psp >> 8) & 0x00FF0000)); - execListQueue.push_back(((GE_CMD_TEXADDR0 + level) << 24) | (psp & 0x00FFFFFF)); + u32 bufwCmd = GE_CMD_TEXBUFWIDTH0 + level; + u32 addrCmd = GE_CMD_TEXADDR0 + level; + execListQueue.push_back((bufwCmd << 24) | ((psp >> 8) & 0x00FF0000) | lastBufw_[level]); + execListQueue.push_back((addrCmd << 24) | (psp & 0x00FFFFFF)); } void DumpExecute::Display(u32 ptr, u32 sz) { @@ -920,6 +962,9 @@ void DumpExecute::Display(u32 ptr, u32 sz) { DisplayBufData *disp = (DisplayBufData *)(pushbuf.data() + ptr); + // Sync up drawing. + SyncStall(); + __DisplaySetFramebuf(disp->topaddr.ptr, disp->linesize, disp->pixelFormat, 1); __DisplaySetFramebuf(disp->topaddr.ptr, disp->linesize, disp->pixelFormat, 0); } From 7a7c655615a4f607ea2e6bc58dc8ca3e4b535eca Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Wed, 13 Jun 2018 11:51:43 +0000 Subject: [PATCH 221/301] MediaEngine: adjust for AVStream.codec deprecation --- Core/HW/MediaEngine.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 1da8800240..62832f65c9 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -455,6 +455,14 @@ bool MediaEngine::setVideoStream(int streamNum, bool force) { return false; } AVCodecContext *m_pCodecCtx = m_pFormatCtx->streams[streamNum]->codec; +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57,33,100) + AVCodecParameters *m_pCodecPar = m_pFormatCtx->streams[streamNum]->codecpar; + + // Update from deprecated public codec context + if (avcodec_parameters_from_context(m_pCodecPar, m_pCodecCtx) < 0) { + return false; + } +#endif // Find the decoder for the video stream AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id); From a5efb85ab7cf47a2b3b2fe75111752b94db201f4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 14 Jun 2018 17:52:44 -0700 Subject: [PATCH 222/301] SaveState: Show warning on old / long use state. Using save states instead of in game saves causes bugs in games, and preserves bugs from bad settings and old PPSSPP versions. This tells users when they might be affected. --- Core/SaveState.cpp | 99 +++++++++++++++++++++++++++++--------- Core/SaveState.h | 13 ++++- Qt/mainwindow.cpp | 4 +- UI/EmuScreen.cpp | 8 +-- UI/NativeApp.cpp | 4 +- UI/PauseScreen.cpp | 6 +-- Windows/MainWindowMenu.cpp | 4 +- 7 files changed, 101 insertions(+), 37 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 30889f8e71..7b3fd33138 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -23,6 +23,7 @@ #include "base/timeutil.h" #include "i18n/i18n.h" #include "thread/threadutil.h" +#include "util/text/parsers.h" #include "Common/FileUtil.h" #include "Common/ChunkFile.h" @@ -240,6 +241,9 @@ namespace SaveState static std::vector pending; static std::mutex mutex; static bool hasLoadedState = false; + static const int STALE_STATE_USES = 10; + static int saveStateGeneration = 0; + static std::string saveStateInitialGitVersion = ""; // TODO: Should this be configurable? static const int REWIND_NUM_STATES = 20; @@ -252,10 +256,21 @@ namespace SaveState void SaveStart::DoState(PointerWrap &p) { - auto s = p.Section("SaveStart", 1); + auto s = p.Section("SaveStart", 1, 2); if (!s) return; + if (s >= 2) { + ++saveStateGeneration; + p.Do(saveStateGeneration); + if (saveStateInitialGitVersion.empty()) + saveStateInitialGitVersion = PPSSPP_GIT_VERSION; + p.Do(saveStateInitialGitVersion); + } else { + saveStateGeneration = 1; + saveStateInitialGitVersion = "v0.0.1"; + } + // Gotta do CoreTiming first since we'll restore into it. CoreTiming::DoState(p); @@ -419,7 +434,7 @@ namespace SaveState } else { I18NCategory *sy = GetI18NCategory("System"); if (callback) - callback(false, sy->T("Failed to load state. Error in the file system."), cbUserData); + callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."), cbUserData); } } @@ -452,8 +467,8 @@ namespace SaveState std::string fnUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_STATE_EXTENSION); std::string shotUndo = GenerateSaveSlotFilename(gameFilename, slot, UNDO_SCREENSHOT_EXTENSION); if (!fn.empty()) { - auto renameCallback = [=](bool status, const std::string &message, void *data) { - if (status) { + auto renameCallback = [=](Status status, const std::string &message, void *data) { + if (status != Status::FAILURE) { if (g_Config.bEnableStateUndo) { DeleteIfExists(fnUndo); RenameIfExists(fn, fnUndo); @@ -476,7 +491,7 @@ namespace SaveState } else { I18NCategory *sy = GetI18NCategory("System"); if (callback) - callback(false, sy->T("Failed to save state. Error in the file system."), cbUserData); + callback(Status::FAILURE, sy->T("Failed to save state. Error in the file system."), cbUserData); } } @@ -618,11 +633,30 @@ namespace SaveState } #endif - bool HasLoadedState() - { + bool HasLoadedState() { return hasLoadedState; } + bool IsStale() { + if (saveStateGeneration >= STALE_STATE_USES) { + // Don't show it every time. + return saveStateGeneration % 5 == 0; + } + return false; + } + + bool IsOldVersion() { + if (saveStateInitialGitVersion.empty()) + return false; + + Version state(saveStateInitialGitVersion); + Version gitVer(PPSSPP_GIT_VERSION); + if (!state.IsValid() || !gitVer.IsValid()) + return false; + + return state < gitVer; + } + void Process() { #ifndef MOBILE_DEVICE @@ -647,7 +681,8 @@ namespace SaveState { Operation &op = operations[i]; CChunkFileReader::Error result; - bool callbackResult; + Status callbackResult; + bool tempResult; std::string callbackMessage; std::string reason; std::string title; @@ -667,8 +702,22 @@ namespace SaveState result = CChunkFileReader::Load(op.filename, PPSSPP_GIT_VERSION, state, &reason); if (result == CChunkFileReader::ERROR_NONE) { callbackMessage = sc->T("Loaded State"); - callbackResult = true; + callbackResult = Status::SUCCESS; hasLoadedState = true; + + if (IsStale()) { + // For anyone wondering why (too long to put on the screen in an osm): + // Using save states instead of saves simulates many hour play sessions. + // Sometimes this exposes game bugs that were rarely seen on real devices, + // because few people played on a real PSP for 10 hours straight. + callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs."); + callbackResult = Status::WARNING; + } else if (IsOldVersion()) { + // Save states also preserve bugs from old PPSSPP versions, so warn. + callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs."); + callbackResult = Status::WARNING; + } + #ifndef MOBILE_DEVICE if (g_Config.bSaveLoadResetsAVdumping) { if (g_Config.bDumpFrames) { @@ -684,10 +733,10 @@ namespace SaveState HandleFailure(); callbackMessage = i18nLoadFailure; ERROR_LOG(SAVESTATE, "Load state failure: %s", reason.c_str()); - callbackResult = false; + callbackResult = Status::FAILURE; } else { callbackMessage = sc->T(reason.c_str(), i18nLoadFailure); - callbackResult = false; + callbackResult = Status::FAILURE; } break; @@ -703,7 +752,7 @@ namespace SaveState result = CChunkFileReader::Save(op.filename, title, PPSSPP_GIT_VERSION, state); if (result == CChunkFileReader::ERROR_NONE) { callbackMessage = sc->T("Saved State"); - callbackResult = true; + callbackResult = Status::SUCCESS; #ifndef MOBILE_DEVICE if (g_Config.bSaveLoadResetsAVdumping) { if (g_Config.bDumpFrames) { @@ -719,16 +768,17 @@ namespace SaveState HandleFailure(); callbackMessage = i18nSaveFailure; ERROR_LOG(SAVESTATE, "Save state failure: %s", reason.c_str()); - callbackResult = false; + callbackResult = Status::FAILURE; } else { callbackMessage = i18nSaveFailure; - callbackResult = false; + callbackResult = Status::FAILURE; } break; case SAVESTATE_VERIFY: - callbackResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE; - if (callbackResult) { + tempResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE; + callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE; + if (tempResult) { INFO_LOG(SAVESTATE, "Verified save state system"); } else { ERROR_LOG(SAVESTATE, "Save state system verification failed"); @@ -740,35 +790,36 @@ namespace SaveState result = rewindStates.Restore(); if (result == CChunkFileReader::ERROR_NONE) { callbackMessage = sc->T("Loaded State"); - callbackResult = true; + callbackResult = Status::SUCCESS; hasLoadedState = true; } else if (result == CChunkFileReader::ERROR_BROKEN_STATE) { // Cripes. Good news is, we might have more. Let's try those too, better than a reset. if (HandleFailure()) { // Well, we did rewind, even if too much... callbackMessage = sc->T("Loaded State"); - callbackResult = true; + callbackResult = Status::SUCCESS; hasLoadedState = true; } else { callbackMessage = i18nLoadFailure; - callbackResult = false; + callbackResult = Status::FAILURE; } } else { callbackMessage = i18nLoadFailure; - callbackResult = false; + callbackResult = Status::FAILURE; } break; case SAVESTATE_SAVE_SCREENSHOT: - callbackResult = TakeGameScreenshot(op.filename.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY); - if (!callbackResult) { + tempResult = TakeGameScreenshot(op.filename.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY); + callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE; + if (!tempResult) { ERROR_LOG(SAVESTATE, "Failed to take a screenshot for the savestate! %s", op.filename.c_str()); } break; default: ERROR_LOG(SAVESTATE, "Savestate failure: unknown operation type %d", op.type); - callbackResult = false; + callbackResult = Status::FAILURE; break; } @@ -790,6 +841,8 @@ namespace SaveState rewindStates.Clear(); hasLoadedState = false; + saveStateGeneration = 0; + saveStateInitialGitVersion.clear(); } void Shutdown() diff --git a/Core/SaveState.h b/Core/SaveState.h index 64c4dbe0c7..ec9efe7bb5 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -23,7 +23,12 @@ namespace SaveState { - typedef std::function Callback; + enum class Status { + FAILURE, + WARNING, + SUCCESS, + }; + typedef std::function Callback; static const int NUM_SLOTS = 5; static const char *STATE_EXTENSION = "ppst"; @@ -79,6 +84,12 @@ namespace SaveState // Returns true if a savestate has been used during this session. bool HasLoadedState(); + // Returns true if the state has been reused instead of real saves many times. + bool IsStale(); + + // Returns true if state is from an older PPSSPP version. + bool IsOldVersion(); + // Check if there's any save stating needing to be done. Normally called once per frame. void Process(); }; diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 904edc6095..620ebc77e9 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -163,10 +163,10 @@ void MainWindow::closeAct() SetGameTitle(""); } -void SaveStateActionFinished(bool result, const std::string &message, void *userdata) +void SaveStateActionFinished(SaveState::Status status, const std::string &message, void *userdata) { // TODO: Improve messaging? - if (!result) + if (status == SaveState::Status::FAILURE) { QMessageBox msgBox; msgBox.setWindowTitle("Load Save State"); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 35cf6a0e0e..e0d84e15f3 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -346,14 +346,14 @@ void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) { RecreateViews(); } -static void AfterSaveStateAction(bool success, const std::string &message, void *) { +static void AfterSaveStateAction(SaveState::Status status, const std::string &message, void *) { if (!message.empty()) { - osm.Show(message, 2.0); + osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } } -static void AfterStateBoot(bool success, const std::string &message, void *ignored) { - AfterSaveStateAction(success, message, ignored); +static void AfterStateBoot(SaveState::Status status, const std::string &message, void *ignored) { + AfterSaveStateAction(status, message, ignored); Core_EnableStepping(false); host->UpdateDisassembly(); } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 97b42ffca1..6176680297 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -590,9 +590,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #endif if (!boot_filename.empty() && stateToLoad != NULL) { - SaveState::Load(stateToLoad, [](bool status, const std::string &message, void *) { + SaveState::Load(stateToLoad, [](SaveState::Status status, const std::string &message, void *) { if (!message.empty()) { - osm.Show(message, 2.0); + osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } }); } diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index b3622fc90f..f066103ba7 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -238,9 +238,9 @@ void SaveSlotView::Draw(UIContext &dc) { UI::LinearLayout::Draw(dc); } -static void AfterSaveStateAction(bool status, const std::string &message, void *) { +static void AfterSaveStateAction(SaveState::Status status, const std::string &message, void *) { if (!message.empty()) { - osm.Show(message, 2.0); + osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } } @@ -373,7 +373,7 @@ void GamePauseScreen::dialogFinished(const Screen *dialog, DialogResult dr) { ScreenshotViewScreen *s = (ScreenshotViewScreen *)dialog; int slot = s->GetSlot(); g_Config.iCurrentStateSlot = slot; - SaveState::LoadSlot(gamePath_, slot, SaveState::Callback(), 0); + SaveState::LoadSlot(gamePath_, slot, &AfterSaveStateAction); finishNextFrame_ = true; } else { diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index caa5ae5511..051d9aa521 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -470,9 +470,9 @@ namespace MainWindow { g_Config.iInternalScreenRotation = rotation; } - static void SaveStateActionFinished(bool result, const std::string &message, void *userdata) { + static void SaveStateActionFinished(SaveState::Status status, const std::string &message, void *userdata) { if (!message.empty()) { - osm.Show(message, 2.0); + osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0); } PostMessage(MainWindow::GetHWND(), WM_USER_SAVESTATE_FINISH, 0, 0); } From 006ef96b195a333af2b9deae4387baf65562f231 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 14 Jun 2018 17:54:13 -0700 Subject: [PATCH 223/301] SaveState: Add a setting to ignore warnings. In case you like to collect old and obscure bugs - everyone's got a hobby. --- Core/Config.cpp | 1 + Core/Config.h | 1 + Core/SaveState.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index b458914d10..c3947bd22c 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -416,6 +416,7 @@ static ConfigSetting cpuSettings[] = { ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, true, true), ReportedConfigSetting("FuncReplacements", &g_Config.bFuncReplacements, true, true, true), ConfigSetting("HideSlowWarnings", &g_Config.bHideSlowWarnings, false, true, false), + ConfigSetting("HideStateWarnings", &g_Config.bHideStateWarnings, false, true, true), ConfigSetting("PreloadFunctions", &g_Config.bPreloadFunctions, false, true, true), ReportedConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0, true, true), diff --git a/Core/Config.h b/Core/Config.h index c7c7e02b13..978b15614f 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -129,6 +129,7 @@ public: bool bForceLagSync; bool bFuncReplacements; bool bHideSlowWarnings; + bool bHideStateWarnings; bool bPreloadFunctions; bool bSeparateSASThread; diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 7b3fd33138..d665e33689 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -705,14 +705,14 @@ namespace SaveState callbackResult = Status::SUCCESS; hasLoadedState = true; - if (IsStale()) { + if (!g_Config.bHideStateWarnings && IsStale()) { // For anyone wondering why (too long to put on the screen in an osm): // Using save states instead of saves simulates many hour play sessions. // Sometimes this exposes game bugs that were rarely seen on real devices, // because few people played on a real PSP for 10 hours straight. callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs."); callbackResult = Status::WARNING; - } else if (IsOldVersion()) { + } else if (!g_Config.bHideStateWarnings && IsOldVersion()) { // Save states also preserve bugs from old PPSSPP versions, so warn. callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs."); callbackResult = Status::WARNING; From 16b11138b4e63c11465452b3ba49be36f4a4efc6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 14 Jun 2018 18:25:52 -0700 Subject: [PATCH 224/301] SaveState: Use latest version if initial missing. This may be useful for debugging or if we decide on a buffer between versions. --- Common/ChunkFile.cpp | 8 +++++++- Common/ChunkFile.h | 4 ++-- Core/SaveState.cpp | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Common/ChunkFile.cpp b/Common/ChunkFile.cpp index 3560c6eeb4..cbc8dc1808 100644 --- a/Common/ChunkFile.cpp +++ b/Common/ChunkFile.cpp @@ -225,7 +225,7 @@ CChunkFileReader::Error CChunkFileReader::GetFileTitle(const std::string &filena return LoadFileHeader(pFile, header, title); } -CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, const char *gitVersion, u8 *&_buffer, size_t &sz, std::string *failureReason) { +CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, std::string *gitVersion, u8 *&_buffer, size_t &sz, std::string *failureReason) { if (!File::Exists(filename)) { *failureReason = "LoadStateDoesntExist"; ERROR_LOG(SAVESTATE, "ChunkReader: File doesn't exist"); @@ -264,6 +264,12 @@ CChunkFileReader::Error CChunkFileReader::LoadFile(const std::string &filename, delete [] buffer; } + if (header.GitVersion[31]) { + *gitVersion = std::string(header.GitVersion, 32); + } else { + *gitVersion = header.GitVersion; + } + return ERROR_NONE; } diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 6fc76ddb29..dba69b0da0 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -622,7 +622,7 @@ public: // Load file template template - static Error Load(const std::string &filename, const char *gitVersion, T& _class, std::string *failureReason) + static Error Load(const std::string &filename, std::string *gitVersion, T& _class, std::string *failureReason) { *failureReason = "LoadStateWrongVersion"; @@ -700,7 +700,7 @@ private: REVISION_CURRENT = REVISION_TITLE, }; - static Error LoadFile(const std::string &filename, const char *gitVersion, u8 *&buffer, size_t &sz, std::string *failureReason); + static Error LoadFile(const std::string &filename, std::string *gitVersion, u8 *&buffer, size_t &sz, std::string *failureReason); static Error SaveFile(const std::string &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz); static Error LoadFileHeader(File::IOFile &pFile, SChunkHeader &header, std::string *title); }; diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index d665e33689..7d4327307d 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -268,7 +268,6 @@ namespace SaveState p.Do(saveStateInitialGitVersion); } else { saveStateGeneration = 1; - saveStateInitialGitVersion = "v0.0.1"; } // Gotta do CoreTiming first since we'll restore into it. @@ -699,7 +698,8 @@ namespace SaveState { case SAVESTATE_LOAD: INFO_LOG(SAVESTATE, "Loading state from %s", op.filename.c_str()); - result = CChunkFileReader::Load(op.filename, PPSSPP_GIT_VERSION, state, &reason); + // Use the state's latest version as a guess for saveStateInitialGitVersion. + result = CChunkFileReader::Load(op.filename, &saveStateInitialGitVersion, state, &reason); if (result == CChunkFileReader::ERROR_NONE) { callbackMessage = sc->T("Loaded State"); callbackResult = Status::SUCCESS; From 0d2de36258f8dcba6bd038092b5dd6befd8c61b0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 14 Jun 2018 20:31:58 -0700 Subject: [PATCH 225/301] GLES: Use accurate GLSL ver in postshader convert. --- GPU/Common/ShaderTranslation.cpp | 6 +----- ext/native/gfx_es2/gpu_features.cpp | 27 ++++++++++++++++++--------- ext/native/gfx_es2/gpu_features.h | 1 + 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/GPU/Common/ShaderTranslation.cpp b/GPU/Common/ShaderTranslation.cpp index 4bc34908be..2614feb453 100644 --- a/GPU/Common/ShaderTranslation.cpp +++ b/GPU/Common/ShaderTranslation.cpp @@ -310,11 +310,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade spirv_cross::ShaderResources resources = glsl.get_shader_resources(); // Set some options. spirv_cross::CompilerGLSL::Options options; - if (gl_extensions.ver[0] >= 4) { - options.version = 400; - } else { - options.version = 300; - } + options.version = gl_extensions.GLSLVersion(); glsl.set_options(options); // Compile to GLSL, ready to give to GL driver. *dest = glsl.compile(); diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 679c0c2b52..5ab7ff80cf 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -54,6 +54,23 @@ bool GLExtensions::VersionGEThan(int major, int minor, int sub) { return gl_extensions.ver[2] >= sub; } +int GLExtensions::GLSLVersion() { + // Used for shader translation and core contexts (Apple drives fail without an exact match.) + if (gl_extensions.VersionGEThan(3, 3)) { + return gl_extensions.ver[0] * 100 + gl_extensions.ver[1] * 10; + } else if (gl_extensions.VersionGEThan(3, 2)) { + return 150; + } else if (gl_extensions.VersionGEThan(3, 1)) { + return 140; + } else if (gl_extensions.VersionGEThan(3, 0)) { + return 130; + } else if (gl_extensions.VersionGEThan(2, 1)) { + return 120; + } else { + return 110; + } +} + void ProcessGPUFeatures() { gl_extensions.bugs = 0; @@ -505,15 +522,7 @@ std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage) { std::string version = ""; if (!gl_extensions.IsGLES && gl_extensions.IsCoreContext) { // We need to add a corresponding #version. Apple drives fail without an exact match. - if (gl_extensions.VersionGEThan(3, 3)) { - version = StringFromFormat("#version %d%d0\n", gl_extensions.ver[0], gl_extensions.ver[1]); - } else if (gl_extensions.VersionGEThan(3, 2)) { - version = "#version 150\n"; - } else if (gl_extensions.VersionGEThan(3, 1)) { - version = "#version 140\n"; - } else { - version = "#version 130\n"; - } + version = StringFromFormat("#version %d\n", gl_extensions.GLSLVersion()); } if (stage == GL_FRAGMENT_SHADER) { temp = version + glsl_fragment_prelude + source; diff --git a/ext/native/gfx_es2/gpu_features.h b/ext/native/gfx_es2/gpu_features.h index b26e923b55..fc1c73a0fd 100644 --- a/ext/native/gfx_es2/gpu_features.h +++ b/ext/native/gfx_es2/gpu_features.h @@ -103,6 +103,7 @@ struct GLExtensions { // greater-or-equal than bool VersionGEThan(int major, int minor, int sub = 0); + int GLSLVersion(); }; extern GLExtensions gl_extensions; From 76ff2adda980c42d5e5bf733e47b18ad66c31699 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 08:06:35 -0700 Subject: [PATCH 226/301] SaveState: Show only after 4 hours. This is 4 hours of the virtual PSP running the game continuously, perhaps from multiple PPSSPP play sessions using save states. --- Core/SaveState.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 7d4327307d..7d336dd993 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -241,7 +241,9 @@ namespace SaveState static std::vector pending; static std::mutex mutex; static bool hasLoadedState = false; - static const int STALE_STATE_USES = 10; + static const int STALE_STATE_USES = 2; + // 4 hours of total gameplay since the virtual PSP started the game. + static const u64 STALE_STATE_TIME = 4 * 3600 * 1000; static int saveStateGeneration = 0; static std::string saveStateInitialGitVersion = ""; @@ -261,8 +263,10 @@ namespace SaveState return; if (s >= 2) { + // This only increments on save, of course. ++saveStateGeneration; p.Do(saveStateGeneration); + // This saves the first git version to create this save state (or generation of save states.) if (saveStateInitialGitVersion.empty()) saveStateInitialGitVersion = PPSSPP_GIT_VERSION; p.Do(saveStateInitialGitVersion); @@ -638,8 +642,7 @@ namespace SaveState bool IsStale() { if (saveStateGeneration >= STALE_STATE_USES) { - // Don't show it every time. - return saveStateGeneration % 5 == 0; + return CoreTiming::GetGlobalTimeUs() > STALE_STATE_TIME; } return false; } From b7ac9a6f373bec66b24155749c9dced035e46135 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 13:29:44 -0700 Subject: [PATCH 227/301] http: Discard output buffer on disconnect. Prevents an assert. --- ext/native/net/sinks.cpp | 6 ++++++ ext/native/net/sinks.h | 1 + ext/native/net/websocket_server.cpp | 2 ++ 3 files changed, 9 insertions(+) diff --git a/ext/native/net/sinks.cpp b/ext/native/net/sinks.cpp index b3ad60fcc9..e011f33453 100644 --- a/ext/native/net/sinks.cpp +++ b/ext/native/net/sinks.cpp @@ -341,6 +341,12 @@ bool OutputSink::Flush(bool allowBlock) { return true; } +void OutputSink::Discard() { + read_ = 0; + write_ = 0; + valid_ = 0; +} + void OutputSink::Drain() { // Avoid small reads if possible. if (valid_ > PRESSURE) { diff --git a/ext/native/net/sinks.h b/ext/native/net/sinks.h index 308c784fb0..fa5546c917 100644 --- a/ext/native/net/sinks.h +++ b/ext/native/net/sinks.h @@ -51,6 +51,7 @@ public: bool Printf(const char *fmt, ...); bool Flush(bool allowBlock = true); + void Discard(); bool Empty(); diff --git a/ext/native/net/websocket_server.cpp b/ext/native/net/websocket_server.cpp index a5fcb81107..397616a058 100644 --- a/ext/native/net/websocket_server.cpp +++ b/ext/native/net/websocket_server.cpp @@ -259,6 +259,8 @@ bool WebSocketServer::Process(float timeout) { // Since select said it was readable, we assume this means disconnect. closeReason_ = WebSocketClose::ABNORMAL; open_ = false; + // Kill any remaining output too. + out_->Discard(); return false; } From c97023568cd20a34f44d659ed88e3c3a8b4ac5f5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 13:30:18 -0700 Subject: [PATCH 228/301] thin3d: Fix requesting BGRA buffers. Should be fine to request BGRA->BGRA. Also, the reverse flags were wrong/ignored on the format. --- GPU/Common/FramebufferCommon.cpp | 4 ++-- ext/native/thin3d/thin3d.cpp | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index db23b60868..6ae4714261 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -1937,7 +1937,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB // TODO: Maybe should handle flipY inside CopyFramebufferToMemorySync somehow? bool flipY = (GetGPUBackend() == GPUBackend::OPENGL && !useBufferedRendering_) ? true : false; - buffer.Allocate(w, h, GE_FORMAT_8888, flipY, true); + buffer.Allocate(w, h, GE_FORMAT_8888, flipY); bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); gpuStats.numReadbacks++; // After a readback we'll have flushed and started over, need to dirty a bunch of things to be safe. @@ -2015,7 +2015,7 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { int w, h; draw_->GetFramebufferDimensions(nullptr, &w, &h); - buffer.Allocate(w, h, GE_FORMAT_8888, false, true); + buffer.Allocate(w, h, GE_FORMAT_8888, false); bool retval = draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); // That may have unbound the framebuffer, rebind to avoid crashes when debugging. RebindFramebuffer(); diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 72afd1cfe3..ddc70e2c7e 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -421,7 +421,18 @@ void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u // Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP. const uint32_t *src32 = (const uint32_t *)src; - if (format == Draw::DataFormat::R8G8B8A8_UNORM) { + if (format == Draw::DataFormat::B8G8R8A8_UNORM) { + uint32_t *dst32 = (uint32_t *)dst; + if (src == dst) { + return; + } else { + for (uint32_t y = 0; y < height; ++y) { + memcpy(dst32, src32, width * 4); + src32 += srcStride; + dst32 += dstStride; + } + } + } else if (format == Draw::DataFormat::R8G8B8A8_UNORM) { uint32_t *dst32 = (uint32_t *)dst; for (uint32_t y = 0; y < height; ++y) { ConvertBGRA8888ToRGBA8888(dst32, src32, width); From 469d8a6a41382b41b5e4890c2096db2697d9e9c2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 13:47:51 -0700 Subject: [PATCH 229/301] GPU: Screenshot output in BGRA if that's native. This is a decent chunk faster. --- GPU/Common/FramebufferCommon.cpp | 8 ++++++-- ext/native/thin3d/VulkanRenderManager.cpp | 3 +-- ext/native/thin3d/thin3d.h | 3 +++ ext/native/thin3d/thin3d_vulkan.cpp | 12 ++++++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 6ae4714261..928f2751ee 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2015,8 +2015,12 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) { int w, h; draw_->GetFramebufferDimensions(nullptr, &w, &h); - buffer.Allocate(w, h, GE_FORMAT_8888, false); - bool retval = draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w); + Draw::DataFormat fmt = draw_->PreferredFramebufferReadbackFormat(nullptr); + // Ignore preferred formats other than BGRA. + if (fmt != Draw::DataFormat::B8G8R8A8_UNORM) + fmt = Draw::DataFormat::R8G8B8A8_UNORM; + buffer.Allocate(w, h, fmt == Draw::DataFormat::R8G8B8A8_UNORM ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_8888_BGRA, false); + bool retval = draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, w, h, fmt, buffer.GetData(), w); // That may have unbound the framebuffer, rebind to avoid crashes when debugging. RebindFramebuffer(); return retval; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index e593442798..48ad0b3d25 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -457,8 +457,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a case VK_FORMAT_R8G8B8A8_UNORM: srcFormat = Draw::DataFormat::R8G8B8A8_UNORM; break; default: _assert_(false); } - } - else { + } else { // Backbuffer. if (!(vulkan_->GetSurfaceCapabilities().supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) { ELOG("Copying from backbuffer not supported, can't take screenshots"); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 3c95516ce0..73088939fb 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -567,6 +567,9 @@ public: virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) { return false; } + virtual DataFormat PreferredFramebufferReadbackFormat(Framebuffer *src) { + return DataFormat::R8G8B8A8_UNORM; + } // These functions should be self explanatory. // Binding a zero render target means binding the backbuffer. diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 065bf4c7f0..26bf8206c9 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -379,6 +379,7 @@ public: void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override; bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter) override; bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) override; + DataFormat PreferredFramebufferReadbackFormat(Framebuffer *src) override; // These functions should be self explanatory. void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override; @@ -1407,6 +1408,17 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, return renderManager_.CopyFramebufferToMemorySync(src ? src->GetFB() : nullptr, aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride); } +DataFormat VKContext::PreferredFramebufferReadbackFormat(Framebuffer *src) { + if (src) { + return DrawContext::PreferredFramebufferReadbackFormat(src); + } + + if (vulkan_->GetSwapchainFormat() == VK_FORMAT_B8G8R8A8_UNORM) { + return Draw::DataFormat::B8G8R8A8_UNORM; + } + return DrawContext::PreferredFramebufferReadbackFormat(src); +} + void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { VKFramebuffer *fb = (VKFramebuffer *)fbo; VKRRenderPassAction color = (VKRRenderPassAction)rp.color; From a0608d7fc5e69312c80e7fba1084a6fd74391073 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 13:53:41 -0700 Subject: [PATCH 230/301] Debugger: Allow GPU actions while stepping. This allows pulling the display/target while paused. It may also allow other actions that work outside a display list. --- Core/Core.cpp | 4 ++++ GPU/Debugger/Stepping.cpp | 35 +++++++++++++++++++++++++++++++++-- GPU/Debugger/Stepping.h | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index bb02a1c08d..51e2fcaab4 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -37,6 +37,7 @@ #include "Core/System.h" #include "Core/Debugger/Breakpoints.h" #include "Core/MIPS/MIPS.h" +#include "GPU/Debugger/Stepping.h" #ifdef _WIN32 #include "Common/CommonWindows.h" @@ -280,6 +281,9 @@ void Core_ProcessStepping() { return; } + // Or any GPU actions. + GPUStepping::SingleStep(); + // We're not inside jit now, so it's safe to clear the breakpoints. CBreakPoints::ClearTemporaryBreakPoints(); host->UpdateDisassembly(); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index c765d15b76..a7f5ebd9e5 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -66,6 +66,9 @@ static void SetPauseAction(PauseAction act, bool waitComplete = true) { pauseAction = act; pauseLock.unlock(); + if (coreState == CORE_STEPPING && act != PAUSE_CONTINUE) + Core_UpdateSingleStep(); + actionComplete = false; pauseWait.notify_all(); while (waitComplete && !actionComplete) { @@ -117,13 +120,41 @@ static void RunPauseAction() { pauseAction = PAUSE_BREAK; } +bool SingleStep() { + std::unique_lock guard(pauseLock); + if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && coreState != CORE_STEPPING) { + // Shutting down, don't try to step. + actionComplete = true; + actionWait.notify_all(); + return false; + } + if (!gpuDebug || pauseAction == PAUSE_CONTINUE) { + actionComplete = true; + actionWait.notify_all(); + return false; + } + + gpuDebug->NotifySteppingEnter(); + isStepping = true; + + RunPauseAction(); + + gpuDebug->NotifySteppingExit(); + isStepping = false; + return true; +} + bool EnterStepping(std::function callback) { std::unique_lock guard(pauseLock); if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) { // Shutting down, don't try to step. + actionComplete = true; + actionWait.notify_all(); return false; } if (!gpuDebug) { + actionComplete = true; + actionWait.notify_all(); return false; } @@ -152,7 +183,7 @@ bool IsStepping() { } static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) { - if (!isStepping) { + if (!isStepping && coreState != CORE_STEPPING) { return false; } @@ -184,7 +215,7 @@ bool GPU_GetCurrentClut(const GPUDebugBuffer *&buffer) { } bool GPU_SetCmdValue(u32 op) { - if (!isStepping) { + if (!isStepping && coreState != CORE_STEPPING) { return false; } diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index d99e8af904..f7fe3e02fb 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -28,6 +28,7 @@ namespace GPUStepping { // Begins stepping and calls callback while inside a lock preparing stepping. // This would be a good place to deliver a message to code that stepping is ready. bool EnterStepping(std::function callback); + bool SingleStep(); bool IsStepping(); bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type); From b5bfef2d03ea820f572a3fec51d06cfe448e9c38 Mon Sep 17 00:00:00 2001 From: hissingshark Date: Sat, 16 Jun 2018 22:15:48 +0100 Subject: [PATCH 231/301] Added Gioteck VX2 detection --- assets/gamecontrollerdb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index 7201505468..aa97cd20a3 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -138,6 +138,7 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b 030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1, 030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7 03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +03000000f0250000c283000010010000,Goodbetterbest Ltd PC USB Controller,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7 0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3, 03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, 030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, From e6917cccf68c42c9612b3cbe27a68d2c9a112114 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 15:07:25 -0700 Subject: [PATCH 232/301] Debugger: Add APIs to retrieve render image. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Debugger/WebSocket.cpp | 2 + .../WebSocket/GPUBufferSubscriber.cpp | 264 ++++++++++++++++++ Core/Debugger/WebSocket/GPUBufferSubscriber.h | 27 ++ GPU/Debugger/Stepping.cpp | 9 + GPU/Debugger/Stepping.h | 1 + android/jni/Android.mk | 1 + ext/native/data/base64.cpp | 2 +- 10 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 Core/Debugger/WebSocket/GPUBufferSubscriber.cpp create mode 100644 Core/Debugger/WebSocket/GPUBufferSubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8827b89b81..386f4e6eaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1414,6 +1414,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/GameBroadcaster.h Core/Debugger/WebSocket/GameSubscriber.cpp Core/Debugger/WebSocket/GameSubscriber.h + Core/Debugger/WebSocket/GPUBufferSubscriber.cpp + Core/Debugger/WebSocket/GPUBufferSubscriber.h Core/Debugger/WebSocket/HLESubscriber.cpp Core/Debugger/WebSocket/HLESubscriber.h Core/Debugger/WebSocket/LogBroadcaster.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d38b535a23..a8765dac4c 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -189,6 +189,7 @@ + @@ -547,6 +548,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 221dd1c738..6e86aea5b2 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -728,6 +728,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1346,6 +1349,9 @@ Debugger\WebSocket + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 7bce20cc5e..4504c827f7 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -52,6 +52,7 @@ #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h" +#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h" #include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/SteppingSubscriber.h" @@ -67,6 +68,7 @@ static const std::vector subscribers({ { &WebSocketCPUCoreInit, nullptr }, { &WebSocketDisasmInit, &WebSocketDisasmShutdown }, { &WebSocketGameInit, nullptr }, + { &WebSocketGPUBufferInit, nullptr }, { &WebSocketHLEInit, nullptr }, { &WebSocketSteppingInit, &WebSocketSteppingShutdown }, }); diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp new file mode 100644 index 0000000000..e6d1105789 --- /dev/null +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#ifndef USING_QT_UI +#include +#include +#endif +#include "data/base64.h" +#include "Common/StringUtils.h" +#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/Screenshot.h" +#include "GPU/Debugger/Stepping.h" + +void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) { + // No need to bind or alloc state, these are all global. + map["gpu.buffer.screenshot"] = &WebSocketGPUBufferScreenshot; + map["gpu.buffer.renderColor"] = &WebSocketGPUBufferRenderColor; + map["gpu.buffer.renderDepth"] = &WebSocketGPUBufferRenderDepth; + map["gpu.buffer.renderStencil"] = &WebSocketGPUBufferRenderStencil; + + return nullptr; +} + +// Note: Calls req.Respond(). Other data can be added afterward. +static bool StreamBufferToDataURI(DebuggerRequest &req, const GPUDebugBuffer &buf, bool includeAlpha) { +#ifdef USING_QT_UI + req.Fail("Not supported on Qt yet, pull requests accepted"); + return false; +#else + u8 *flipbuffer = nullptr; + u32 w = (u32)-1; + u32 h = (u32)-1; + const u8 *buffer = ConvertBufferToScreenshot(buf, includeAlpha, flipbuffer, w, h); + if (!buffer) { + req.Fail("Internal error converting buffer for PNG encode"); + return false; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) { + req.Fail("Internal error setting up PNG encoder (png_ptr)"); + return false; + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, nullptr); + req.Fail("Internal error setting up PNG encoder (info_ptr)"); + return false; + } + + // Speed. Wireless N should give 35 KB/ms. For most devices, zlib/filters will cost more. + png_set_compression_strategy(png_ptr, Z_RLE); + png_set_compression_level(png_ptr, 1); + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); + + auto &json = req.Respond(); + json.writeInt("width", w); + json.writeInt("height", h); + + // Start a value... + json.writeRaw("uri", ""); + req.Flush(); + // Now we'll write it directly to the stream. + req.ws->AddFragment(false, "\"data:image/png;base64,"); + + struct Context { + DebuggerRequest *req; + uint8_t buf[3]; + size_t bufSize; + }; + Context ctx = { &req, {}, 0 }; + + auto write = [](png_structp png_ptr, png_bytep data, png_size_t length) { + auto ctx = (Context *)png_get_io_ptr(png_ptr); + auto &req = *ctx->req; + + // If we buffered some bytes, fill to 3 bytes for a clean base64 encode. + // This way we don't have padding. + while (length > 0 && ctx->bufSize > 0 && ctx->bufSize != 3) { + ctx->buf[ctx->bufSize++] = data[0]; + data++; + length--; + } + + if (ctx->bufSize == 3) { + req.ws->AddFragment(false, Base64Encode(ctx->buf, ctx->bufSize)); + ctx->bufSize = 0; + } + assert(ctx->bufSize == 0 || length == 0); + + // Save bytes that would result in padding for next time. + size_t toBuffer = length % 3; + for (size_t i = 0; i < toBuffer; ++i) { + ctx->buf[i] = data[length - toBuffer + i]; + ctx->bufSize++; + } + + if (length > toBuffer) { + req.ws->AddFragment(false, Base64Encode(data, length - toBuffer)); + } + }; + auto flush = [](png_structp png_ptr) { + // Nothing, just here to prevent stdio flush. + }; + + png_bytep *row_pointers = new png_bytep[h]; + u32 stride = includeAlpha ? w * 4 : w * 3; + for (u32 i = 0; i < h; ++i) { + row_pointers[i] = (u8 *)buffer + stride * i; + } + + png_set_write_fn(png_ptr, &ctx, write, flush); + int colorType = includeAlpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB; + png_set_IHDR(png_ptr, info_ptr, w, h, 8, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_rows(png_ptr, info_ptr, row_pointers); + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + delete [] row_pointers; + delete [] flipbuffer; + + if (ctx.bufSize > 0) { + req.ws->AddFragment(false, Base64Encode(ctx.buf, ctx.bufSize)); + ctx.bufSize = 0; + } + + // End the string. + req.ws->AddFragment(false, "\""); + return true; +#endif +} + +static std::string DescribeFormat(GPUDebugBufferFormat fmt) { + switch (fmt) { + case GPU_DBG_FORMAT_565: return "B5G6R5_UNORM_PACK16"; + case GPU_DBG_FORMAT_5551: return "A1B5G5R5_UNORM_PACK16"; + case GPU_DBG_FORMAT_4444: return "A4B4G4R4_UNORM_PACK16"; + case GPU_DBG_FORMAT_8888: return "R8G8B8A8_UNORM"; + + case GPU_DBG_FORMAT_565_REV: return "R5G6B5_UNORM_PACK16"; + case GPU_DBG_FORMAT_5551_REV: return "R5G5B5A1_UNORM_PACK16"; + case GPU_DBG_FORMAT_4444_REV: return "R4G4B4A4_UNORM_PACK16"; + + case GPU_DBG_FORMAT_5551_BGRA: return "A1R5G5B5_UNORM_PACK16"; + case GPU_DBG_FORMAT_4444_BGRA: return "A4R4G4B4_UNORM_PACK16"; + case GPU_DBG_FORMAT_8888_BGRA: return "B8G8R8A8_UNORM"; + + case GPU_DBG_FORMAT_FLOAT: return "D32F"; + case GPU_DBG_FORMAT_16BIT: return "D16"; + case GPU_DBG_FORMAT_8BIT: return "S8"; + case GPU_DBG_FORMAT_24BIT_8X: return "D24_X8"; + case GPU_DBG_FORMAT_24X_8BIT: return "X24_S8"; + + case GPU_DBG_FORMAT_FLOAT_DIV_256: return "D32F_DIV_256"; + case GPU_DBG_FORMAT_24BIT_8X_DIV_256: return "D32F_X8_DIV_256"; + + case GPU_DBG_FORMAT_888_RGB: return "R8G8B8_UNORM"; + + case GPU_DBG_FORMAT_INVALID: + case GPU_DBG_FORMAT_BRSWAP_FLAG: + default: + return "UNDEFINED"; + } +} + +// Note: Calls req.Respond(). Other data can be added afterward. +static bool StreamBufferToBase64(DebuggerRequest &req, const GPUDebugBuffer &buf) { + size_t length = buf.GetStride() * buf.GetHeight(); + + auto &json = req.Respond(); + json.writeInt("width", buf.GetStride()); + json.writeInt("height", buf.GetHeight()); + json.writeBool("flipped", buf.GetFlipped()); + json.writeString("format", DescribeFormat(buf.GetFormat())); + + // Start a value without any actual data yet... + json.writeRaw("base64", ""); + req.Flush(); + + // Now we'll write it directly to the stream. + req.ws->AddFragment(false, "\""); + // 65535 is an "even" number of base64 characters. + static const size_t CHUNK_SIZE = 65535; + for (size_t i = 0; i < length; i += CHUNK_SIZE) { + size_t left = std::min(length - i, CHUNK_SIZE); + req.ws->AddFragment(false, Base64Encode(buf.GetData() + i, left)); + } + req.ws->AddFragment(false, "\""); + + return true; +} + +static void GenericStreamBuffer(DebuggerRequest &req, std::function func) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + if (coreState != CORE_STEPPING && !GPUStepping::IsStepping()) { + return req.Fail("Neither CPU or GPU is stepping"); + } + + bool includeAlpha = false; + if (!req.ParamBool("alpha", &includeAlpha, DebuggerParamType::OPTIONAL)) + return; + std::string type = "uri"; + if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL)) + return; + if (type != "uri" && type != "base64") + return req.Fail("Parameter 'type' must be either 'uri' or 'base64'"); + + const GPUDebugBuffer *buf = nullptr; + if (!func(buf)) { + return req.Fail("Could not download output"); + } + assert(buf != nullptr); + + if (type == "base64") { + StreamBufferToBase64(req, *buf); + } else if (type == "uri") { + StreamBufferToDataURI(req, *buf, includeAlpha); + } else { + _assert_(false); + } +} + +void WebSocketGPUBufferScreenshot(DebuggerRequest &req) { + GenericStreamBuffer(req, [](const GPUDebugBuffer *&buf) { + return GPUStepping::GPU_GetOutputFramebuffer(buf); + }); +} + +void WebSocketGPUBufferRenderColor(DebuggerRequest &req) { + GenericStreamBuffer(req, [](const GPUDebugBuffer *&buf) { + return GPUStepping::GPU_GetCurrentFramebuffer(buf, GPU_DBG_FRAMEBUF_RENDER); + }); +} + +void WebSocketGPUBufferRenderDepth(DebuggerRequest &req) { + GenericStreamBuffer(req, [](const GPUDebugBuffer *&buf) { + return GPUStepping::GPU_GetCurrentDepthbuffer(buf); + }); +} + +void WebSocketGPUBufferRenderStencil(DebuggerRequest &req) { + GenericStreamBuffer(req, [](const GPUDebugBuffer *&buf) { + return GPUStepping::GPU_GetCurrentStencilbuffer(buf); + }); +} diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.h b/Core/Debugger/WebSocket/GPUBufferSubscriber.h new file mode 100644 index 0000000000..166b206105 --- /dev/null +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.h @@ -0,0 +1,27 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map); + +void WebSocketGPUBufferScreenshot(DebuggerRequest &req); +void WebSocketGPUBufferRenderColor(DebuggerRequest &req); +void WebSocketGPUBufferRenderDepth(DebuggerRequest &req); +void WebSocketGPUBufferRenderStencil(DebuggerRequest &req); diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index a7f5ebd9e5..644d833750 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -28,6 +28,7 @@ namespace GPUStepping { enum PauseAction { PAUSE_CONTINUE, PAUSE_BREAK, + PAUSE_GETOUTPUTBUF, PAUSE_GETFRAMEBUF, PAUSE_GETDEPTHBUF, PAUSE_GETSTENCILBUF, @@ -87,6 +88,10 @@ static void RunPauseAction() { case PAUSE_BREAK: break; + case PAUSE_GETOUTPUTBUF: + bufferResult = gpuDebug->GetOutputFramebuffer(bufferFrame); + break; + case PAUSE_GETFRAMEBUF: bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame, bufferType); break; @@ -192,6 +197,10 @@ static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPU return bufferResult; } +bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer) { + return GetBuffer(buffer, PAUSE_GETOUTPUTBUF, bufferFrame); +} + bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type) { bufferType = type; return GetBuffer(buffer, PAUSE_GETFRAMEBUF, bufferFrame); diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index f7fe3e02fb..fb24cf072a 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -31,6 +31,7 @@ namespace GPUStepping { bool SingleStep(); bool IsStepping(); + bool GPU_GetOutputFramebuffer(const GPUDebugBuffer *&buffer); bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer, GPUDebugFramebufferType type); bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer); bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 4501775884..ddf7c7a799 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -307,6 +307,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/DisasmSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ + $(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ diff --git a/ext/native/data/base64.cpp b/ext/native/data/base64.cpp index 82d428468c..8df7abaa1e 100644 --- a/ext/native/data/base64.cpp +++ b/ext/native/data/base64.cpp @@ -4,7 +4,7 @@ std::string Base64Encode(const uint8_t *p, size_t sz) { const char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t unpaddedLength = (4 * sz + 3) / 3; + size_t unpaddedLength = (4 * sz + 2) / 3; std::string result; result.resize((unpaddedLength + 3) & ~3, '='); From f0694ba11516675366ad9d45d861f7a5df7443f9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 15:42:42 -0700 Subject: [PATCH 233/301] Debugger: Add some docs for new APIs. --- .../WebSocket/BreakpointSubscriber.cpp | 94 +++++++++++++++++++ .../WebSocket/GPUBufferSubscriber.cpp | 68 ++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index aa6681cb82..ffe1797ce5 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -121,6 +121,18 @@ struct WebSocketCPUBreakpointParams { } }; +// Add a new CPU instruction breakpoint (cpu.breakpoint.add) +// +// Parameters: +// - address: unsigned integer address of instruction to break at. +// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips. +// - log: optional boolean, whether to log when this breakpoint trips. +// - condition: optional string expression to evaluate - breakpoint does not trip if false. +// - logFormat: optional string to log when breakpoint trips, may include {expression} parts. +// +// Response (same event name) with no extra data. +// +// Note: will replace any breakpoint at the same address. void WebSocketCPUBreakpointAdd(DebuggerRequest &req) { WebSocketCPUBreakpointParams params; if (!params.Parse(req)) @@ -131,6 +143,16 @@ void WebSocketCPUBreakpointAdd(DebuggerRequest &req) { req.Respond(); } +// Update a CPU instruction breakpoint (cpu.breakpoint.update) +// +// Parameters: +// - address: unsigned integer address of instruction to break at. +// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips. +// - log: optional boolean, whether to log when this breakpoint trips. +// - condition: optional string expression to evaluate - breakpoint does not trip if false. +// - logFormat: optional string to log when breakpoint trips, may include {expression} parts. +// +// Response (same event name) with no extra data. void WebSocketCPUBreakpointUpdate(DebuggerRequest &req) { WebSocketCPUBreakpointParams params; if (!params.Parse(req)) @@ -143,6 +165,12 @@ void WebSocketCPUBreakpointUpdate(DebuggerRequest &req) { req.Respond(); } +// Remove a CPU instruction breakpoint (cpu.breakpoint.remove) +// +// Parameters: +// - address: unsigned integer address of instruction to break at. +// +// Response (same event name) with no extra data. void WebSocketCPUBreakpointRemove(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); @@ -156,6 +184,19 @@ void WebSocketCPUBreakpointRemove(DebuggerRequest &req) { req.Respond(); } +// List all CPU instruction breakpoints (cpu.breakpoint.list) +// +// No parameters. +// +// Response (same event name): +// - breakpoints: array of objects, each with properties: +// - address: unsigned integer address of instruction to break at. +// - enabled: boolean, whether to actually enter stepping when this breakpoint trips. +// - log: optional boolean, whether to log when this breakpoint trips. +// - condition: null, or string expression to evaluate - breakpoint does not trip if false. +// - logFormat: null, or string to log when breakpoint trips, may include {expression} parts. +// - symbol: null, or string label or symbol at breakpoint address. +// - code: string disassembly of breakpoint address. void WebSocketCPUBreakpointList(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); @@ -278,6 +319,22 @@ struct WebSocketMemoryBreakpointParams { } }; +// Add a new memory breakpoint (memory.breakpoint.add) +// +// Parameters: +// - address: unsigned integer address for the start of the memory range. +// - size: unsigned integer specifying size of memory range. +// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips. +// - log: optional boolean, whether to log when this breakpoint trips. +// - read: optional boolean, whether to trip on any read to this address. +// - write: optional boolean, whether to trip on any write to this address. +// - change: optional boolean, whether to trip on a write to this address which modifies data +// (or any write that may modify data.) +// - logFormat: optional string to log when breakpoint trips, may include {expression} parts. +// +// Response (same event name) with no extra data. +// +// Note: will replace any breakpoint that has the same start address and size. void WebSocketMemoryBreakpointAdd(DebuggerRequest &req) { WebSocketMemoryBreakpointParams params; if (!params.Parse(req)) @@ -288,6 +345,20 @@ void WebSocketMemoryBreakpointAdd(DebuggerRequest &req) { req.Respond(); } +// Update a memory breakpoint (memory.breakpoint.update) +// +// Parameters: +// - address: unsigned integer address for the start of the memory range. +// - size: unsigned integer specifying size of memory range. +// - enabled: optional boolean, whether to actually enter stepping when this breakpoint trips. +// - log: optional boolean, whether to log when this breakpoint trips. +// - read: optional boolean, whether to trip on any read to this address. +// - write: optional boolean, whether to trip on any write to this address. +// - change: optional boolean, whether to trip on a write to this address which modifies data +// (or any write that may modify data.) +// - logFormat: optional string to log when breakpoint trips, may include {expression} parts. +// +// Response (same event name) with no extra data. void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req) { WebSocketMemoryBreakpointParams params; if (!params.Parse(req)) @@ -302,6 +373,13 @@ void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req) { req.Respond(); } +// Remove a memory breakpoint (memory.breakpoint.remove) +// +// Parameters: +// - address: unsigned integer address for the start of the memory range. +// - size: unsigned integer specifying size of memory range. +// +// Response (same event name) with no extra data. void WebSocketMemoryBreakpointRemove(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); @@ -318,6 +396,22 @@ void WebSocketMemoryBreakpointRemove(DebuggerRequest &req) { req.Respond(); } +// List all memory breakpoints (memory.breakpoint.list) +// +// No parameters. +// +// Response (same event name): +// - breakpoints: array of objects, each with properties: +// - address: unsigned integer address for the start of the memory range. +// - size: unsigned integer specifying size of memory range. +// - enabled: boolean, whether to actually enter stepping when this breakpoint trips. +// - log: optional boolean, whether to log when this breakpoint trips. +// - read: optional boolean, whether to trip on any read to this address. +// - write: optional boolean, whether to trip on any write to this address. +// - change: optional boolean, whether to trip on a write to this address which modifies data +// (or any write that may modify data.) +// - logFormat: null, or string to log when breakpoint trips, may include {expression} parts. +// - symbol: null, or string label or symbol at breakpoint address. void WebSocketMemoryBreakpointList(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); diff --git a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp index e6d1105789..915426755b 100644 --- a/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp @@ -239,24 +239,92 @@ static void GenericStreamBuffer(DebuggerRequest &req, std::function Date: Sat, 16 Jun 2018 16:04:25 -0700 Subject: [PATCH 234/301] GLES: Restore stencil/depth readback for debugger. Only works on desktop GL. --- ext/native/thin3d/GLQueueRunner.cpp | 26 ++++++++++++++++++++------ ext/native/thin3d/thin3d_gl.cpp | 4 ++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index fa423a5780..6f7ccc917d 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -1085,13 +1085,27 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { CHECK_GL_ERROR_IF_DEBUG(); - // Always read back in 8888 format. - const GLuint internalFormat = GL_RGBA; - const GLuint format = GL_RGBA; - const GLuint type = GL_UNSIGNED_BYTE; - const int srcAlignment = 4; + // Always read back in 8888 format for the color aspect. + GLuint internalFormat = GL_RGBA; + GLuint format = GL_RGBA; + GLuint type = GL_UNSIGNED_BYTE; + int srcAlignment = 4; int dstAlignment = (int)DataFormatSizeInBytes(pass.readback.dstFormat); +#ifndef USING_GLES2 + if (pass.readback.aspectMask & GL_DEPTH_BUFFER_BIT) { + internalFormat = GL_DEPTH_COMPONENT; + format = GL_DEPTH_COMPONENT; + type = GL_FLOAT; + srcAlignment = 4; + } else if (pass.readback.aspectMask & GL_STENCIL_BUFFER_BIT) { + internalFormat = GL_STENCIL_INDEX; + format = GL_STENCIL_INDEX; + type = GL_UNSIGNED_BYTE; + srcAlignment = 1; + } +#endif + int pixelStride = pass.readback.srcRect.w; // Apply the correct alignment. glPixelStorei(GL_PACK_ALIGNMENT, srcAlignment); @@ -1102,7 +1116,7 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) { GLRect2D rect = pass.readback.srcRect; - bool convert = pass.readback.dstFormat != DataFormat::R8G8B8A8_UNORM; + bool convert = internalFormat == GL_RGBA && pass.readback.dstFormat != DataFormat::R8G8B8A8_UNORM; int tempSize = srcAlignment * rect.w * rect.h; int readbackSize = dstAlignment * rect.w * rect.h; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 8fdd744b83..f803aeb069 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -720,6 +720,10 @@ static void LogReadPixelsError(GLenum error) { #endif bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { + if (gl_extensions.IsGLES && (channelBits & FB_COLOR_BIT) == 0) { + // Can't readback depth or stencil on GLES. + return false; + } OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; GLuint aspect = 0; if (channelBits & FB_COLOR_BIT) From 3b8a8476546f988e87f084448afdaa3b49316a21 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Jun 2018 12:27:00 -0700 Subject: [PATCH 235/301] SDL: Allow more EGL configs. This attempts to choose the "best" config using weights. --- SDL/SDLGLGraphicsContext.cpp | 105 +++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index a225831a60..3466fe1bea 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -1,3 +1,4 @@ +#include #include "SDLGLGraphicsContext.h" #include "Core/Config.h" #include "Core/System.h" @@ -72,34 +73,94 @@ int8_t EGL_Open() { return 0; } -int8_t EGL_Init() { - EGLConfig g_eglConfig; - EGLint g_numConfigs = 0; - EGLint attrib_list[]= { - // TODO: Should cycle through fallbacks, like on Android -#ifdef USING_FBDEV - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, +#ifndef EGL_OPENGL_ES3_BIT_KHR +#define EGL_OPENGL_ES3_BIT_KHR (1 << 6) #endif - EGL_DEPTH_SIZE, 16, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + +EGLConfig EGL_FindConfig(int &contextVersion) { + std::vector configs; + EGLint numConfigs = 0; + + EGLBoolean result = eglGetConfigs(g_eglDisplay, nullptr, 0, &numConfigs); + if (result != EGL_TRUE || numConfigs == 0) { + return nullptr; + } + + configs.resize(numConfigs); + EGLBoolean result = eglGetConfigs(g_eglDisplay, &configs[0], numConfigs, &numConfigs); + if (result != EGL_TRUE || numConfigs == 0) { + return nullptr; + } + + EGLConfig best = nullptr; + int bestScore = 0; + int bestContextVersion = 0; + for (const EGLConfig &config : configs) { + auto readConfig = [&](EGLint attr) -> EGLint { + EGLint val = 0; + eglGetConfigAttrib(g_eglDisplay, config, attr, &val); + return val; + }; + + int colorScore = readConfig(EGL_RED_SIZE) + readConfig(EGL_BLUE_SIZE) + readConfig(EGL_GREEN_SIZE); + int alphaScore = readConfig(EGL_ALPHA_SIZE); + int depthScore = readConfig(EGL_DEPTH_SIZE); + int levelScore = readConfig(EGL_LEVEL) == 0 ? 100 : 0; + int samplesScore = readConfig(EGL_SAMPLES) == 0 ? 100 : 0; + int sampleBufferScore = readConfig(EGL_SAMPLE_BUFFERS) == 0 ? 100 : 0; + int stencilScore = readConfig(EGL_STENCIL_SIZE); + int transparentScore = readConfig(EGL_TRANSPARENT_TYPE) == EGL_NONE ? 50 : 0; + + EGLint caveat = readConfig(EGL_CONFIG_CAVEAT); + int caveatScore = caveat == EGL_NONE ? 100 : (caveat == EGL_NON_CONFORMANT ? 50 : 0); + + EGLint renderable = readConfig(EGL_RENDERABLE_TYPE); + bool renderableGLES3 = (renderable & EGL_OPENGL_ES3_BIT_KHR) != 0; + bool renderableGLES2 = (renderable & EGL_OPENGL_ES2_BIT) != 0; + bool renderableGL = (renderable & EGL_OPENGL_BIT) != 0; #ifdef USING_GLES2 - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + int renderableScore = renderableGLES3 ? 100 : (renderableGLES2 ? 80 : 0); #else - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + int renderableScore = renderableGL ? 100 : (renderableGLES3 ? 80 : 0); #endif - EGL_SAMPLE_BUFFERS, 0, - EGL_SAMPLES, 0, - EGL_NONE}; - const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + int score = 0; + // Here's a good place to play with the weights to pick a better config. + score += (colorScore + alphaScore) * 10; + score += depthScore * 5 + stencilScore; + score += levelScore + samplesScore + sampleBufferScore + transparentScore; + score += caveatScore + renderableScore; - EGLBoolean result = eglChooseConfig(g_eglDisplay, attrib_list, &g_eglConfig, 1, &g_numConfigs); - if (result != EGL_TRUE || g_numConfigs == 0) EGL_ERROR("Unable to query for available configs.", true); + if (score > bestScore) { + bestScore = score; + best = config; + bestContextVersion = renderableGLES3 ? 3 : (renderableGLES2 ? 2 : 0); + } + } - g_eglContext = eglCreateContext(g_eglDisplay, g_eglConfig, NULL, attributes ); - if (g_eglContext == EGL_NO_CONTEXT) EGL_ERROR("Unable to create GLES context!", true); + contextVersion = bestContextVersion; + return best; +} + +int8_t EGL_Init() { + int contextVersion = 0; + EGLConfig eglConfig = EGL_FindConfig(); + if (!eglConfig) { + EGL_ERROR("Unable to find a usable EGL config.", true); + } + + EGLint contextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, contextVersion, + EGL_NONE, + }; + if (contextVersion == 0) { + ctx_attributes[0] = EGL_NONE; + } + + g_eglContext = eglCreateContext(g_eglDisplay, eglConfig, nullptr, contextAttributes); + if (g_eglContext == EGL_NO_CONTEXT) { + EGL_ERROR("Unable to create GLES context!", true); + } #if !defined(USING_FBDEV) && !defined(__APPLE__) //Get the SDL window handle @@ -109,7 +170,7 @@ int8_t EGL_Init() { #else g_Window = (NativeWindowType)NULL; #endif - g_eglSurface = eglCreateWindowSurface(g_eglDisplay, g_eglConfig, g_Window, 0); + g_eglSurface = eglCreateWindowSurface(g_eglDisplay, eglConfig, g_Window, nullptr); if (g_eglSurface == EGL_NO_SURFACE) EGL_ERROR("Unable to create EGL surface!", true); From 84273ff5eb717e040f762d69c64a6d2971baedc9 Mon Sep 17 00:00:00 2001 From: hissingshark Date: Sun, 10 Jun 2018 00:32:05 -0700 Subject: [PATCH 236/301] SDL: Buildfixes for EGL lookup. --- SDL/SDLGLGraphicsContext.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 3466fe1bea..2884534e61 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -77,7 +77,7 @@ int8_t EGL_Open() { #define EGL_OPENGL_ES3_BIT_KHR (1 << 6) #endif -EGLConfig EGL_FindConfig(int &contextVersion) { +EGLConfig EGL_FindConfig(int *contextVersion) { std::vector configs; EGLint numConfigs = 0; @@ -87,7 +87,7 @@ EGLConfig EGL_FindConfig(int &contextVersion) { } configs.resize(numConfigs); - EGLBoolean result = eglGetConfigs(g_eglDisplay, &configs[0], numConfigs, &numConfigs); + result = eglGetConfigs(g_eglDisplay, &configs[0], numConfigs, &numConfigs); if (result != EGL_TRUE || numConfigs == 0) { return nullptr; } @@ -112,7 +112,7 @@ EGLConfig EGL_FindConfig(int &contextVersion) { int transparentScore = readConfig(EGL_TRANSPARENT_TYPE) == EGL_NONE ? 50 : 0; EGLint caveat = readConfig(EGL_CONFIG_CAVEAT); - int caveatScore = caveat == EGL_NONE ? 100 : (caveat == EGL_NON_CONFORMANT ? 50 : 0); + int caveatScore = caveat == EGL_NONE ? 100 : (caveat == EGL_NON_CONFORMANT_CONFIG ? 50 : 0); EGLint renderable = readConfig(EGL_RENDERABLE_TYPE); bool renderableGLES3 = (renderable & EGL_OPENGL_ES3_BIT_KHR) != 0; @@ -138,13 +138,13 @@ EGLConfig EGL_FindConfig(int &contextVersion) { } } - contextVersion = bestContextVersion; + *contextVersion = bestContextVersion; return best; } int8_t EGL_Init() { int contextVersion = 0; - EGLConfig eglConfig = EGL_FindConfig(); + EGLConfig eglConfig = EGL_FindConfig(&contextVersion); if (!eglConfig) { EGL_ERROR("Unable to find a usable EGL config.", true); } @@ -154,7 +154,7 @@ int8_t EGL_Init() { EGL_NONE, }; if (contextVersion == 0) { - ctx_attributes[0] = EGL_NONE; + contextAttributes[0] = EGL_NONE; } g_eglContext = eglCreateContext(g_eglDisplay, eglConfig, nullptr, contextAttributes); From 1b417590514ac4e45f2ac3333e842f3d1ab20ba8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 16:19:17 -0700 Subject: [PATCH 237/301] SDL: Avoid alpha on Mali/ARM devices using GLES. See #10534. --- SDL/SDLGLGraphicsContext.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 2884534e61..e3dc209e3f 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -92,6 +92,11 @@ EGLConfig EGL_FindConfig(int *contextVersion) { return nullptr; } + // Mali (ARM) seems to have compositing issues with alpha backbuffers. + // EGL_TRANSPARENT_TYPE doesn't help. + const char *vendorName = eglQueryString(g_eglDisplay, EGL_VENDOR); + const bool avoidAlphaGLES = vendorName && !strcmp(vendorName, "ARM"); + EGLConfig best = nullptr; int bestScore = 0; int bestContextVersion = 0; @@ -119,17 +124,23 @@ EGLConfig EGL_FindConfig(int *contextVersion) { bool renderableGLES2 = (renderable & EGL_OPENGL_ES2_BIT) != 0; bool renderableGL = (renderable & EGL_OPENGL_BIT) != 0; #ifdef USING_GLES2 - int renderableScore = renderableGLES3 ? 100 : (renderableGLES2 ? 80 : 0); + int renderableScoreGLES = renderableGLES3 ? 100 : (renderableGLES2 ? 80 : 0); + int renderableScoreGL = 0; #else - int renderableScore = renderableGL ? 100 : (renderableGLES3 ? 80 : 0); + int renderableScoreGLES = 0; + int renderableScoreGL = renderableGL ? 100 : (renderableGLES3 ? 80 : 0); #endif + if (avoidAlphaGLES && renderableScoreGLES > 0) { + alphaScore = 8 - alphaScore; + } + int score = 0; // Here's a good place to play with the weights to pick a better config. score += (colorScore + alphaScore) * 10; score += depthScore * 5 + stencilScore; score += levelScore + samplesScore + sampleBufferScore + transparentScore; - score += caveatScore + renderableScore; + score += caveatScore + renderableScoreGLES + renderableScoreGL; if (score > bestScore) { bestScore = score; From 4a92db4cd65dfe7ac87fe35cc4cfd9fb79d82d5b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 10:50:11 -0700 Subject: [PATCH 238/301] Debugger: Update to latest armips. Reduces warning noise now that it's included in the build. --- android/jni/Android.mk | 5 ++++- ext/armips | 2 +- ext/cmake/armips/CMakeLists.txt | 10 ++++++++-- ext/libarmips.vcxproj | 10 ++++++++-- ext/libarmips.vcxproj.filters | 30 ++++++++++++++++++++++++------ 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 4501775884..402036ec2d 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -421,7 +421,8 @@ LIBARMIPS_FILES := \ $(SRC)/ext/armips/Archs/ARM/Arm.cpp \ $(SRC)/ext/armips/Archs/ARM/ArmOpcodes.cpp \ $(SRC)/ext/armips/Archs/ARM/ArmParser.cpp \ - $(SRC)/ext/armips/Archs/ARM/ArmRelocator.cpp \ + $(SRC)/ext/armips/Archs/ARM/ArmElfRelocator.cpp \ + $(SRC)/ext/armips/Archs/ARM/ArmExpressionFunctions.cpp \ $(SRC)/ext/armips/Archs/ARM/CArmInstruction.cpp \ $(SRC)/ext/armips/Archs/ARM/CThumbInstruction.cpp \ $(SRC)/ext/armips/Archs/ARM/Pool.cpp \ @@ -429,6 +430,8 @@ LIBARMIPS_FILES := \ $(SRC)/ext/armips/Archs/MIPS/CMipsInstruction.cpp \ $(SRC)/ext/armips/Archs/MIPS/Mips.cpp \ $(SRC)/ext/armips/Archs/MIPS/MipsElfFile.cpp \ + $(SRC)/ext/armips/Archs/MIPS/MipsElfRelocator.cpp \ + $(SRC)/ext/armips/Archs/MIPS/MipsExpressionFunctions.cpp \ $(SRC)/ext/armips/Archs/MIPS/MipsMacros.cpp \ $(SRC)/ext/armips/Archs/MIPS/MipsOpcodes.cpp \ $(SRC)/ext/armips/Archs/MIPS/MipsParser.cpp \ diff --git a/ext/armips b/ext/armips index 8b4cadaf62..3a55cf3600 160000 --- a/ext/armips +++ b/ext/armips @@ -1 +1 @@ -Subproject commit 8b4cadaf62d7de42d374056fc6aafc555f2bc7dc +Subproject commit 3a55cf3600d6f328ae0897699ac06818af2905a4 diff --git a/ext/cmake/armips/CMakeLists.txt b/ext/cmake/armips/CMakeLists.txt index 3a1689334a..96ef055b47 100644 --- a/ext/cmake/armips/CMakeLists.txt +++ b/ext/cmake/armips/CMakeLists.txt @@ -78,8 +78,10 @@ add_library(armips ${ARMIPS_PATH}/Archs/ARM/ArmOpcodes.h ${ARMIPS_PATH}/Archs/ARM/ArmParser.cpp ${ARMIPS_PATH}/Archs/ARM/ArmParser.h - ${ARMIPS_PATH}/Archs/ARM/ArmRelocator.cpp - ${ARMIPS_PATH}/Archs/ARM/ArmRelocator.h + ${ARMIPS_PATH}/Archs/ARM/ArmElfRelocator.cpp + ${ARMIPS_PATH}/Archs/ARM/ArmElfRelocator.h + ${ARMIPS_PATH}/Archs/ARM/ArmExpressionFunctions.cpp + ${ARMIPS_PATH}/Archs/ARM/ArmExpressionFunctions.h ${ARMIPS_PATH}/Archs/ARM/CArmInstruction.cpp ${ARMIPS_PATH}/Archs/ARM/CArmInstruction.h ${ARMIPS_PATH}/Archs/ARM/CThumbInstruction.cpp @@ -95,6 +97,10 @@ add_library(armips ${ARMIPS_PATH}/Archs/MIPS/Mips.h ${ARMIPS_PATH}/Archs/MIPS/MipsElfFile.cpp ${ARMIPS_PATH}/Archs/MIPS/MipsElfFile.h + ${ARMIPS_PATH}/Archs/MIPS/MipsElfRelocator.cpp + ${ARMIPS_PATH}/Archs/MIPS/MipsElfRelocator.h + ${ARMIPS_PATH}/Archs/MIPS/MipsExpressionFunctions.cpp + ${ARMIPS_PATH}/Archs/MIPS/MipsExpressionFunctions.h ${ARMIPS_PATH}/Archs/MIPS/MipsMacros.cpp ${ARMIPS_PATH}/Archs/MIPS/MipsMacros.h ${ARMIPS_PATH}/Archs/MIPS/MipsOpcodes.cpp diff --git a/ext/libarmips.vcxproj b/ext/libarmips.vcxproj index 4fbff29f20..688f859d77 100644 --- a/ext/libarmips.vcxproj +++ b/ext/libarmips.vcxproj @@ -234,9 +234,10 @@ + + - @@ -244,6 +245,8 @@ + + @@ -287,9 +290,10 @@ + + - @@ -297,6 +301,8 @@ + + diff --git a/ext/libarmips.vcxproj.filters b/ext/libarmips.vcxproj.filters index bb35b6fa4f..e1c8ef9a3a 100644 --- a/ext/libarmips.vcxproj.filters +++ b/ext/libarmips.vcxproj.filters @@ -42,9 +42,6 @@ Archs\ARM - - Archs\ARM - Archs\ARM @@ -163,6 +160,18 @@ Core + + Archs\ARM + + + Archs\MIPS + + + Archs\ARM + + + Archs\MIPS + @@ -174,9 +183,6 @@ Archs\ARM - - Archs\ARM - Archs\ARM @@ -301,5 +307,17 @@ Core + + Archs\ARM + + + Archs\MIPS + + + Archs\ARM + + + Archs\MIPS + \ No newline at end of file From 6592c6222a219ce9e6f019ee3135ff08e1128d7c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 10:56:42 -0700 Subject: [PATCH 239/301] http: Check fread() result to avoid warning. This will cause the length not to match the result if it happens, but the client should interpret that as a server error. Also a similar error in headless. --- Core/WebServer.cpp | 3 ++- headless/StubHost.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index f5fdf68754..284c1c0333 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -163,7 +163,8 @@ static void RegisterDiscHandlers(http::Server *http, std::unordered_mapPush(buf, chunklen); } fclose(fp); diff --git a/headless/StubHost.cpp b/headless/StubHost.cpp index 82698770da..dc3520013e 100644 --- a/headless/StubHost.cpp +++ b/headless/StubHost.cpp @@ -71,7 +71,9 @@ void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) FILE *bmp = File::OpenCFile(comparisonScreenshot_, "rb"); if (bmp) { - fread(&header, sizeof(header), 1, bmp); + if (fread(&header, sizeof(header), 1, bmp) != 1) { + SendOrCollectDebugOutput("Failed to read original screenshot header.\n"); + } fclose(bmp); } From 82dc4c0b726c2c175d530d7eae9ea4caf80a80e6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 11:19:32 -0700 Subject: [PATCH 240/301] Debugger: Fix some format warnings in udis86. --- ext/udis86/syn.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ext/udis86/syn.c b/ext/udis86/syn.c index 630c21f0c7..c66e2433be 100644 --- a/ext/udis86/syn.c +++ b/ext/udis86/syn.c @@ -23,6 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include "types.h" #include "decode.h" #include "syn.h" @@ -137,14 +138,14 @@ ud_syn_print_addr(struct ud *u, uint64_t addr) name = u->sym_resolver(u, addr, &offset); if (name) { if (offset) { - ud_asmprintf(u, "%s%+" FMT64 "d", name, offset); + ud_asmprintf(u, "%s%+" PRId64, name, offset); } else { ud_asmprintf(u, "%s", name); } return; } } - ud_asmprintf(u, "0x%" FMT64 "x", addr); + ud_asmprintf(u, "0x%" PRIx64, addr); } @@ -171,7 +172,7 @@ ud_syn_print_imm(struct ud* u, const struct ud_operand *op) default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ } } - ud_asmprintf(u, "0x%" FMT64 "x", v); + ud_asmprintf(u, "0x%" PRIx64, v); } uint64_t @@ -193,7 +194,7 @@ ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *op, int sign) case 64: v = op->lval.uqword; break; default: UD_ASSERT(!"invalid offset"); v = 0; /* keep cc happy */ } - ud_asmprintf(u, "0x%" FMT64 "x", v); + ud_asmprintf(u, "0x%" PRIx64, v); } else { int64_t v; UD_ASSERT(op->offset != 64); @@ -206,9 +207,9 @@ ud_syn_print_mem_disp(struct ud* u, const struct ud_operand *op, int sign) if (op->base == UD_R_RIP) { ud_syn_print_addr(u, ud_syn_rip_target(u, op)); } else if (v < 0) { - ud_asmprintf(u, "-0x%" FMT64 "x", -v); + ud_asmprintf(u, "-0x%" PRIx64, -v); } else if (v > 0) { - ud_asmprintf(u, "%s0x%" FMT64 "x", sign? "+" : "", v); + ud_asmprintf(u, "%s0x%" PRIx64, sign? "+" : "", v); } } } From d1fa9b97a3a89613dee63111291d74007042cb38 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 11:21:58 -0700 Subject: [PATCH 241/301] GPU: Remove some unused fields. --- GPU/D3D11/GPU_D3D11.h | 1 - GPU/Directx9/GPU_DX9.h | 1 - GPU/GLES/GPU_GLES.h | 1 - GPU/Vulkan/DrawEngineVulkan.cpp | 6 +++--- GPU/Vulkan/DrawEngineVulkan.h | 4 +--- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index acb4dd3e07..781cc5f1fb 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -83,5 +83,4 @@ private: ShaderManagerD3D11 *shaderManagerD3D11_; int lastVsync_; - int vertexCost_ = 0; }; diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index 3fb4be032d..5bf58fd136 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -82,7 +82,6 @@ private: ShaderManagerDX9 *shaderManagerDX9_; int lastVsync_; - int vertexCost_ = 0; }; } // namespace DX9 diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index 6789491090..9f70cbec16 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -92,5 +92,4 @@ private: #ifdef _WIN32 int lastVsync_; #endif - int vertexCost_ = 0; }; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index f071f41c64..a85d32e52e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -167,7 +167,7 @@ void DrawEngineVulkan::InitDeviceObjects() { vertexCache_ = new VulkanPushBuffer(vulkan_, VERTEX_CACHE_SIZE); - tessDataTransfer = new TessellationDataTransferVulkan(vulkan_, draw_); + tessDataTransfer = new TessellationDataTransferVulkan(vulkan_); } DrawEngineVulkan::~DrawEngineVulkan() { @@ -994,8 +994,8 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { } } -DrawEngineVulkan::TessellationDataTransferVulkan::TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) - : TessellationDataTransfer(), vulkan_(vulkan), draw_(draw) { +DrawEngineVulkan::TessellationDataTransferVulkan::TessellationDataTransferVulkan(VulkanContext *vulkan) + : TessellationDataTransfer(), vulkan_(vulkan) { } DrawEngineVulkan::TessellationDataTransferVulkan::~TessellationDataTransferVulkan() { diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 2459a7cb5a..494a4ccd4b 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -280,7 +280,7 @@ private: // Hardware tessellation class TessellationDataTransferVulkan : public TessellationDataTransfer { public: - TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw); + TessellationDataTransferVulkan(VulkanContext *vulkan); ~TessellationDataTransferVulkan(); void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; } @@ -299,10 +299,8 @@ private: private: VulkanContext *vulkan_; - Draw::DrawContext *draw_; VulkanPushBuffer *push_; // Updated each frame. - int size_ = 0; uint32_t offset_ = 0; uint32_t range_ = 0; VkBuffer buf_ = VK_NULL_HANDLE; From e4127890ec6dea5e79d35b69a446f8e8c4caf3f5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 11:30:22 -0700 Subject: [PATCH 242/301] Vulkan: Fix some missing switch warnings. Might as well prefer a virtual GPU over a CPU maybe... --- Common/Vulkan/VulkanContext.cpp | 5 +++++ Common/Vulkan/VulkanLoader.cpp | 2 ++ UI/GameSettingsScreen.cpp | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index ffe7ba5a3f..3b43e80c02 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -444,12 +444,17 @@ int VulkanContext::GetBestPhysicalDevice() { case VK_PHYSICAL_DEVICE_TYPE_CPU: score += 1; break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + score += 2; + break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: score += 20; break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: score += 10; break; + default: + break; } if (props.vendorID == VULKAN_VENDOR_AMD) { score += 5; diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index 0d41785a28..04755dc555 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -313,6 +313,8 @@ bool VulkanMayBeAvailable() { case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: anyGood = true; break; + default: + break; } // TODO: Should also check queuefamilyproperties for a GRAPHICS queue family? Oh well. } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 0ad1a44cf5..2834218006 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -104,8 +104,9 @@ bool DoesBackendSupportHWTess() { case GPUBackend::VULKAN: case GPUBackend::DIRECT3D11: return true; + default: + return false; } - return false; } static std::string PostShaderTranslateName(const char *value) { From 80312582d12b7e57574222ae0e208752ca94c45b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 11:32:17 -0700 Subject: [PATCH 243/301] GPU: Avoid a switch case warning. --- GPU/Common/DepalettizeShaderCommon.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 84723b6e24..19a14c2dfc 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -286,6 +286,9 @@ void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguag case HLSL_DX9: GenerateDepalShaderFloat(buffer, pixelFormat, language); break; + case HLSL_D3D11_LEVEL9: + default: + _assert_msg_(G3D, false, "Depal shader language not supported: %d", (int)language); } } From b281da9fa7ad203a4f9ad2f3cc046a052d5047ae Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 11:38:31 -0700 Subject: [PATCH 244/301] Jpeg: Avoid signed shift warnings. This seems easier to read anyway... --- ext/native/ext/jpge/jpgd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/ext/jpge/jpgd.cpp b/ext/native/ext/jpge/jpgd.cpp index 5fdc1ba52a..4f1d57096d 100644 --- a/ext/native/ext/jpge/jpgd.cpp +++ b/ext/native/ext/jpge/jpgd.cpp @@ -563,7 +563,7 @@ inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits) // Tables and macro used to fully decode the DPCM differences. static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; -static const int s_extend_offset[16] = { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; +static const int s_extend_offset[16] = { 0, -(1<<1) + 1, -(1<<2) + 1, -(1<<3) + 1, -(1<<4) + 1, -(1<<5) + 1, -(1<<6) + 1, -(1<<7) + 1, -(1<<8) + 1, -(1<<9) + 1, -(1<<10) + 1, -(1<<11) + 1, -(1<<12) + 1, -(1<<13) + 1, -(1<<14) + 1, -(1<<15) + 1 }; //static const int s_extend_mask[] = { 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16) }; // The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this) #define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x)) @@ -3169,4 +3169,4 @@ unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *w return decompress_jpeg_image_from_stream(&file_stream, width, height, actual_comps, req_comps); } -} // namespace jpgd \ No newline at end of file +} // namespace jpgd From ae7f5d7824fd8ace05cb61fb17e6b5fd906476d2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 11:42:45 -0700 Subject: [PATCH 245/301] native: Avoid an absolute value error. Presumably this was the goal. Not tested. --- ext/native/ext/rg_etc1/rg_etc1.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/ext/rg_etc1/rg_etc1.cpp b/ext/native/ext/rg_etc1/rg_etc1.cpp index e003f14863..a19bdd1a7b 100644 --- a/ext/native/ext/rg_etc1/rg_etc1.cpp +++ b/ext/native/ext/rg_etc1/rg_etc1.cpp @@ -1805,7 +1805,7 @@ namespace rg_etc1 { if (block_inten[0] > m_pSorted_luma[n - 1]) { - const uint min_error = (uint)(labs(block_inten[0] - m_pSorted_luma[n - 1])); + const uint min_error = (uint)(labs((int)block_inten[0] - (int)m_pSorted_luma[n - 1])); if (min_error >= trial_solution.m_error) continue; } @@ -1819,7 +1819,7 @@ namespace rg_etc1 { if (m_pSorted_luma[0] > block_inten[3]) { - const uint min_error = (uint)(labs(m_pSorted_luma[0] - block_inten[3])); + const uint min_error = (uint)(labs((int)m_pSorted_luma[0] - (int)block_inten[3])); if (min_error >= trial_solution.m_error) continue; } From 495a6359c833af6370c691b8758bf860c13453d9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 17:49:40 -0700 Subject: [PATCH 246/301] http: Don't crash on unclean client disconnect. This happens even when doing http ISO streaming. --- ext/native/net/http_server.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/native/net/http_server.cpp b/ext/native/net/http_server.cpp index c77dd37169..f7cae6cd30 100644 --- a/ext/native/net/http_server.cpp +++ b/ext/native/net/http_server.cpp @@ -59,7 +59,9 @@ Request::~Request() { CHECK(in_->Empty()); delete in_; - CHECK(out_->Empty()); + if (!out_->Empty()) { + ELOG("Output not empty - connection abort?"); + } delete out_; } From f6036195d822a9963f33d7a4a091b74aea5aa661 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 17:40:37 -0700 Subject: [PATCH 247/301] GLES: Cleanup stencil upload w/h handling. In case a buffer has been resized recently, we want to upload just the detected drawable area, probably. Before this was inconsistent depending on the render resolution. --- GPU/Common/FramebufferCommon.h | 7 +++++-- GPU/GLES/StencilBufferGLES.cpp | 11 ++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 468701663a..4f19debd01 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -86,10 +86,13 @@ struct VirtualFramebuffer { // width/height: The detected size of the current framebuffer. u16 width; u16 height; - // renderWidth/renderHeight: The actual size we render at. May be scaled to render at higher resolutions. + // renderWidth/renderHeight: The scaled size we render at. May be scaled to render at higher resolutions. + // The physical buffer may be larger than renderWidth/renderHeight. u16 renderWidth; u16 renderHeight; - // bufferWidth/bufferHeight: The actual (but non scaled) size of the buffer we render to. May only be bigger than width/height. + // bufferWidth/bufferHeight: The pre-scaling size of the buffer itself. May only be bigger than width/height. + // Actual physical buffer is this size times the render resolution multiplier. + // The buffer may be used to render a width or height from 0 to these values without being recreated. u16 bufferWidth; u16 bufferHeight; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index fcbba39970..a97fc593d5 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -146,27 +146,28 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe // Our fragment shader (and discard) is slow. Since the source is 1x, we can stencil to 1x. // Then after we're done, we'll just blit it across and stretch it there. - if (dstBuffer->bufferWidth == dstBuffer->renderWidth || !dstBuffer->fbo) { + if (dstBuffer->width == dstBuffer->renderWidth || !dstBuffer->fbo) { useBlit = false; } - u16 w = useBlit ? dstBuffer->bufferWidth : dstBuffer->renderWidth; - u16 h = useBlit ? dstBuffer->bufferHeight : dstBuffer->renderHeight; + u16 w = useBlit ? dstBuffer->width : dstBuffer->renderWidth; + u16 h = useBlit ? dstBuffer->height : dstBuffer->renderHeight; Draw::Framebuffer *blitFBO = nullptr; if (useBlit) { blitFBO = GetTempFBO(TempFBO::COPY, w, h, Draw::FBO_8888); draw_->BindFramebufferAsRenderTarget(blitFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); } else if (dstBuffer->fbo) { - draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); + draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::DONT_CARE }); } render_->SetViewport({ 0, 0, (float)w, (float)h, 0.0f, 1.0f }); float u1 = 1.0f; float v1 = 1.0f; - MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); + MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height, u1, v1); textureCacheGL_->ForgetLastTexture(); // We must bind the program after starting the render pass, and set the color mask after clearing. + render_->SetScissor({ 0, 0, w, h }); render_->SetDepth(false, false, GL_ALWAYS); render_->Clear(0, 0, 0, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, 0x8, 0, 0, 0, 0); render_->SetStencilFunc(GL_TRUE, GL_ALWAYS, 0xFF, 0xFF); From 9f4cd3fcdb3ff2a2705239af9824d7f050e99909 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 17:42:02 -0700 Subject: [PATCH 248/301] GLES: Add missing semantics for stencil upload. Makes #10634 work for GLES properly. --- GPU/GLES/StencilBufferGLES.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index a97fc593d5..9dac4c9a3f 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -126,12 +126,15 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe std::vector shaders; shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "stencil")); shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "stencil")); + std::vector semantics; + semantics.push_back({ 0, "a_position" }); + semantics.push_back({ 1, "a_texcoord0" }); std::vector queries; queries.push_back({ &u_stencilUploadTex, "tex" }); queries.push_back({ &u_stencilValue, "u_stencilValue" }); std::vector inits; inits.push_back({ &u_stencilUploadTex, 0, 0 }); - stencilUploadProgram_ = render_->CreateProgram(shaders, {}, queries, inits, false); + stencilUploadProgram_ = render_->CreateProgram(shaders, semantics, queries, inits, false); for (auto iter : shaders) { render_->DeleteShader(iter); } From bd13f6b9066c0f3ee0cd4dbaa9940f7c6787a5c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 17:45:20 -0700 Subject: [PATCH 249/301] Vulkan: Workaround stencil upload bug on Adreno. We write a static depth value, which will be ignored, to force the driver to support discard. --- GPU/Vulkan/StencilBufferVulkan.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index 35661711d5..7ce1e00fe5 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -27,6 +27,8 @@ #include "GPU/Vulkan/TextureCacheVulkan.h" #include "GPU/Vulkan/VulkanUtil.h" +// This shader references gl_FragDepth to prevent early fragment tests. +// They shouldn't happen since it uses discard, but Adreno detects that incorrectly - see #10634. static const char *stencil_fs = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable @@ -38,6 +40,8 @@ layout (location = 0) in vec2 v_texcoord0; layout (location = 0) out vec4 fragColor0; void main() { + gl_FragDepth = 0.0; + if (u_stencilValue == 0) { fragColor0 = vec4(0.0); } else { From 03edd6129a5d73fd10936fd6cec6af52210b7243 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 20:51:34 -0700 Subject: [PATCH 250/301] GLES: Cut down on use of USING_GLES2. --- GPU/GLES/DepalettizeShaderGLES.cpp | 24 +++++++++++------------- GPU/GLES/FramebufferManagerGLES.cpp | 4 ++-- Windows/GEDebugger/SimpleGLWindow.cpp | 2 -- Windows/GEDebugger/VertexPreview.cpp | 2 -- ext/native/thin3d/thin3d_gl.cpp | 14 +++++--------- headless/Headless.cpp | 4 ---- 6 files changed, 18 insertions(+), 32 deletions(-) diff --git a/GPU/GLES/DepalettizeShaderGLES.cpp b/GPU/GLES/DepalettizeShaderGLES.cpp index f2b32c7678..52b1458b13 100644 --- a/GPU/GLES/DepalettizeShaderGLES.cpp +++ b/GPU/GLES/DepalettizeShaderGLES.cpp @@ -25,15 +25,10 @@ #include "GPU/GLES/TextureCacheGLES.h" #include "GPU/Common/DepalettizeShaderCommon.h" -#ifdef _WIN32 -#define SHADERLOG -#endif - static const char *depalVShader100 = -#ifdef USING_GLES2 -"#version 100\n" +"#ifdef GL_ES\n" "precision highp float;\n" -#endif +"#endif\n" "attribute vec4 a_position;\n" "attribute vec2 a_texcoord0;\n" "varying vec2 v_texcoord0;\n" @@ -43,12 +38,9 @@ static const char *depalVShader100 = "}\n"; static const char *depalVShader300 = -#ifdef USING_GLES2 -"#version 300 es\n" +"#ifdef GL_ES\n" "precision highp float;\n" -#else -"#version 330\n" -#endif +"#endif\n" "in vec4 a_position;\n" "in vec2 a_texcoord0;\n" "out vec2 v_texcoord0;\n" @@ -76,7 +68,13 @@ void DepalShaderCacheGLES::DeviceRestore(Draw::DrawContext *draw) { bool DepalShaderCacheGLES::CreateVertexShader() { std::string src(useGL3_ ? depalVShader300 : depalVShader100); - vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, src, "depal"); + std::string prelude; + if (gl_extensions.IsGLES) { + prelude = useGL3_ ? "#version 300 es\n" : "#version 100\n"; + } else if (useGL3_) { + prelude = "#version 330\n"; + } + vertexShader_ = render_->CreateShader(GL_VERTEX_SHADER, prelude + src, "depal"); return true; } diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 62b1da391c..e15aa36983 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -54,9 +54,9 @@ static const char tex_fs[] = "#define gl_FragColor fragColor0\n" "out vec4 fragColor0;\n" "#endif\n" -#ifdef USING_GLES2 + "#ifdef GL_ES\n" "precision mediump float;\n" -#endif + "#endif\n" "uniform sampler2D sampler0;\n" "varying vec2 v_texcoord0;\n" "void main() {\n" diff --git a/Windows/GEDebugger/SimpleGLWindow.cpp b/Windows/GEDebugger/SimpleGLWindow.cpp index f9c06b5c02..c24613711d 100644 --- a/Windows/GEDebugger/SimpleGLWindow.cpp +++ b/Windows/GEDebugger/SimpleGLWindow.cpp @@ -55,9 +55,7 @@ static const char tex_fs[] = "}\n"; static const char basic_vs[] = -#ifndef USING_GLES2 "#version 120\n" -#endif "attribute vec4 a_position;\n" "attribute vec2 a_texcoord0;\n" "uniform mat4 u_viewproj;\n" diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index 6eeef756dc..c17c6d3638 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -36,9 +36,7 @@ static const char preview_fs[] = "}\n"; static const char preview_vs[] = -#ifndef USING_GLES2 "#version 120\n" -#endif "attribute vec4 a_position;\n" "uniform mat4 u_viewproj;\n" "void main() {\n" diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index f803aeb069..63e8bb809d 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -347,11 +347,10 @@ public: return caps_; } uint32_t GetSupportedShaderLanguages() const override { -#if defined(USING_GLES2) - return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_ES_300; -#else - return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_410; -#endif + if (gl_extensions.IsGLES) + return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_ES_300; + else + return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_410; } uint32_t GetDataFormatSupport(DataFormat fmt) const override; @@ -1163,11 +1162,8 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const { case DataFormat::B4G4R4A4_UNORM_PACK16: return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS; // native support case DataFormat::A4R4G4B4_UNORM_PACK16: -#ifndef USING_GLES2 // Can support this if _REV formats are supported. - return FMT_TEXTURE; -#endif - return 0; + return gl_extensions.IsGLES ? 0 : FMT_TEXTURE; case DataFormat::R8G8B8A8_UNORM: return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT | FMT_AUTOGEN_MIPS; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index d81a229d16..da101c27ce 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -350,11 +350,7 @@ int main(int argc, const char* argv[]) g_Config.bAutoSaveSymbolMap = false; g_Config.iRenderingMode = 1; g_Config.bHardwareTransform = true; -#ifdef USING_GLES2 - g_Config.iAnisotropyLevel = 0; -#else g_Config.iAnisotropyLevel = 0; // When testing mipmapping we really don't want this. -#endif g_Config.bVertexCache = true; g_Config.bTrueColor = true; g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; From 767886c4a3d18ba5419e4de71d28e56b2fccef76 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 21:56:18 -0700 Subject: [PATCH 251/301] Android: Fix C++ EGL code path with threaded GLES. Still has the same problems as before. --- android/jni/AndroidEGLContext.cpp | 10 ++++-- android/jni/AndroidEGLContext.h | 20 ++++++++++++ android/jni/app-android.cpp | 51 +++++++++++++++++++++++-------- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/android/jni/AndroidEGLContext.cpp b/android/jni/AndroidEGLContext.cpp index 1a40994cf7..a3071e1b05 100644 --- a/android/jni/AndroidEGLContext.cpp +++ b/android/jni/AndroidEGLContext.cpp @@ -47,15 +47,21 @@ bool AndroidEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int des CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); SetGPUBackend(GPUBackend::OPENGL); + renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); bool success = draw_->CreatePresets(); // There will always be a GLSL compiler capable of compiling these. assert(success); return true; } -void AndroidEGLGraphicsContext::Shutdown() { +void AndroidEGLGraphicsContext::ShutdownFromRenderThread() { + ILOG("AndroidEGLGraphicsContext::Shutdown"); + renderManager_->WaitUntilQueueIdle(); + renderManager_ = nullptr; // owned by draw_. delete draw_; draw_ = nullptr; - NativeShutdownGraphics(); +} + +void AndroidEGLGraphicsContext::Shutdown() { gl->ClearCurrent(); gl->Shutdown(); delete gl; diff --git a/android/jni/AndroidEGLContext.h b/android/jni/AndroidEGLContext.h index 2bc9d5341c..35ae334294 100644 --- a/android/jni/AndroidEGLContext.h +++ b/android/jni/AndroidEGLContext.h @@ -1,5 +1,6 @@ #pragma once +#include "thin3d/GLRenderManager.h" #include "AndroidGraphicsContext.h" #include "Common/GL/GLInterfaceBase.h" @@ -7,6 +8,7 @@ class AndroidEGLGraphicsContext : public AndroidGraphicsContext { public: AndroidEGLGraphicsContext() : draw_(nullptr), wnd_(nullptr), gl(nullptr) {} bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override; + void ShutdownFromRenderThread() override; void Shutdown() override; void SwapBuffers() override; void SwapInterval(int interval) override {} @@ -18,8 +20,26 @@ public: return draw_ != nullptr; } + void ThreadStart() override { + renderManager_->ThreadStart(); + } + + bool ThreadFrame() override { + return renderManager_->ThreadFrame(); + } + + void ThreadEnd() override { + renderManager_->ThreadEnd(); + } + + void StopThread() override { + renderManager_->WaitUntilQueueIdle(); + renderManager_->StopThread(); + } + private: Draw::DrawContext *draw_; ANativeWindow *wnd_; cInterfaceBase *gl; + GLRenderManager *renderManager_ = nullptr; }; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9675eba147..38fc3f6582 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -442,9 +442,12 @@ retry: switch (g_Config.iGPUBackend) { case (int)GPUBackend::OPENGL: useCPUThread = true; - _assert_(javaGL); - ILOG("NativeApp.init() -- creating OpenGL context (JavaGL)"); - graphicsContext = new AndroidJavaEGLGraphicsContext(); + if (javaGL) { + ILOG("NativeApp.init() -- creating OpenGL context (JavaGL)"); + graphicsContext = new AndroidJavaEGLGraphicsContext(); + } else { + graphicsContext = new AndroidEGLGraphicsContext(); + } break; case (int)GPUBackend::VULKAN: { @@ -521,7 +524,8 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_pause(JNIEnv *, jclass) { } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { - if (useCPUThread && graphicsContext) { + if (renderer_inited && useCPUThread && graphicsContext) { + // Only used in Java EGL path. EmuThreadStop(); while (graphicsContext->ThreadFrame()) { continue; @@ -963,31 +967,54 @@ retry: } if (!exitRenderLoop) { - NativeInitGraphics(graphicsContext); + if (!useCPUThread) { + NativeInitGraphics(graphicsContext); + } + graphicsContext->ThreadStart(); renderer_inited = true; } - while (!exitRenderLoop) { + if (!exitRenderLoop) { static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } + } - NativeUpdate(); + if (useCPUThread) { + ELOG("Running graphics loop"); + while (!exitRenderLoop) { + // This is the "GPU thread". + graphicsContext->ThreadFrame(); + graphicsContext->SwapBuffers(); + } + } else { + while (!exitRenderLoop) { + NativeUpdate(); - NativeRender(graphicsContext); - time_update(); + NativeRender(graphicsContext); + time_update(); - graphicsContext->SwapBuffers(); + graphicsContext->SwapBuffers(); - ProcessFrameCommands(env); + ProcessFrameCommands(env); + } } ILOG("Leaving EGL/Vulkan render loop."); - NativeShutdownGraphics(); + if (useCPUThread) { + EmuThreadStop(); + while (graphicsContext->ThreadFrame()) { + continue; + } + EmuThreadJoin(); + } else { + NativeShutdownGraphics(); + } renderer_inited = false; + graphicsContext->ThreadEnd(); // Shut the graphics context down to the same state it was in when we entered the render thread. ILOG("Shutting down graphics context from render thread..."); From 4aa4db611ed175204d0084a371638cabcb38ff07 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 17 Jun 2018 22:19:11 -0700 Subject: [PATCH 252/301] Android: Allow desktop GL if available. For example, on the SHIELD. --- Common/GL/GLInterface/EGL.cpp | 33 ++++++++++++++++++++++++----- android/jni/AndroidEGLContext.cpp | 4 +++- ext/native/gfx_es2/gpu_features.cpp | 6 +++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Common/GL/GLInterface/EGL.cpp b/Common/GL/GLInterface/EGL.cpp index 90a67dc285..fb215af598 100644 --- a/Common/GL/GLInterface/EGL.cpp +++ b/Common/GL/GLInterface/EGL.cpp @@ -202,14 +202,21 @@ bool cInterfaceEGL::ChooseAndCreate(void *window_handle, bool core, bool use565) EGLint ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE, 0 + EGL_NONE, 0, + EGL_NONE, 0, + EGL_NONE, 0, + EGL_NONE, 0, }; switch (s_opengl_mode) { case MODE_OPENGL: EGL_ILOG("Setting RENDERABLE_TYPE to EGL_OPENGL_BIT"); attribs[1] = EGL_OPENGL_BIT; - ctx_attribs[0] = EGL_NONE; + // 1 will be major version, and 3 the minor version. + ctx_attribs[2] = 0x30FB; /* EGL_CONTEXT_MINOR_VERSION_KHR */ + // Let's always use a core profile here. + ctx_attribs[4] = 0x30FD; /* EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR */ + ctx_attribs[5] = 1; /* EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR */ break; case MODE_OPENGLES2: EGL_ILOG("Setting RENDERABLE_TYPE to EGL_OPENGL_ES2_BIT"); @@ -230,7 +237,7 @@ bool cInterfaceEGL::ChooseAndCreate(void *window_handle, bool core, bool use565) EGL_ILOG("Calling eglChooseConfig to get number of configs (use16bit=%d)...", (int)use565); EGLConfig *configs; - EGLint num_configs; + EGLint num_configs = 0; if (!eglChooseConfig(egl_dpy, attribs, NULL, 0, &num_configs) || num_configs == 0) { EGL_ILOG("Error: couldn't get a number of configs. Trying with fallback config (no stencil, not specifying transparent:none)\n"); attribsFallback[1] = attribs[1]; @@ -298,7 +305,23 @@ bool cInterfaceEGL::ChooseAndCreate(void *window_handle, bool core, bool use565) s = eglQueryString(egl_dpy, EGL_CLIENT_APIS); EGL_ILOG("EGL_CLIENT_APIS = %s\n", s); - egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs); + + if (s_opengl_mode == MODE_OPENGL) { + EGL_ILOG("Finding a good GL version"); + egl_ctx = nullptr; + for (int minor = 6; minor >= 0 && !egl_ctx; --minor) { + ctx_attribs[1] = 4; + ctx_attribs[3] = minor; + egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs); + } + if (!egl_ctx) { + ctx_attribs[1] = 3; + ctx_attribs[3] = 3; + egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs); + } + } else { + egl_ctx = eglCreateContext(egl_dpy, configs[chosenConfig], EGL_NO_CONTEXT, ctx_attribs); + } if (!egl_ctx) { EGL_ILOG("Error: eglCreateContext failed: %s\n", EGLGetErrorString(eglGetError())); delete[] configs; @@ -339,7 +362,7 @@ bool cInterfaceEGL::Create(void *window_handle, bool core, bool use565) { if (s_opengl_mode == MODE_DETECT || s_opengl_mode == MODE_DETECT_ES) DetectMode(); - if (!ChooseAndCreate(window_handle, core, use565) && s_opengl_mode == MODE_OPENGLES3) { + if (!ChooseAndCreate(window_handle, core, use565) && (s_opengl_mode == MODE_OPENGLES3 || s_opengl_mode == MODE_OPENGL)) { // Fallback to ES 2.0 and try again. s_opengl_mode = MODE_OPENGLES2; if (!ChooseAndCreate(window_handle, core, use565)) { diff --git a/android/jni/AndroidEGLContext.cpp b/android/jni/AndroidEGLContext.cpp index a3071e1b05..3eb13c8689 100644 --- a/android/jni/AndroidEGLContext.cpp +++ b/android/jni/AndroidEGLContext.cpp @@ -22,7 +22,7 @@ bool AndroidEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int des // Apparently we still have to set this through Java through setFixedSize on the bufferHolder for it to take effect... gl->SetBackBufferDimensions(backbufferWidth, backbufferHeight); - gl->SetMode(MODE_DETECT_ES); + gl->SetMode(MODE_DETECT); bool use565 = false; @@ -44,6 +44,8 @@ bool AndroidEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int des return false; } gl->MakeCurrent(); + if (gl->GetMode() == GLInterfaceMode::MODE_OPENGL) + SetGLCoreContext(true); CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); SetGPUBackend(GPUBackend::OPENGL); diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 5ab7ff80cf..01b8c6c570 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -116,7 +116,7 @@ void CheckGLExtensions() { gl_extensions.IsCoreContext = useCoreContext; #ifdef USING_GLES2 - gl_extensions.IsGLES = true; + gl_extensions.IsGLES = !useCoreContext; #endif const char *renderer = (const char *)glGetString(GL_RENDERER); @@ -201,6 +201,10 @@ void CheckGLExtensions() { // Most of it could be enabled on lower GPUs as well, but let's start this way. if (gl_extensions.VersionGEThan(4, 3, 0)) { gl_extensions.GLES3 = true; +#ifdef USING_GLES2 + // Try to load up the other funcs if we're not using glew. + gl3stubInit(); +#endif } } else { // Start by assuming we're at 2.0. From 3090360692af3fd64f208d21ff7b91951b520898 Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 19 Jun 2018 18:42:33 +0430 Subject: [PATCH 253/301] Make HideSaveWarnings global (#11196) --- Core/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index c3947bd22c..2751ae147f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -416,7 +416,7 @@ static ConfigSetting cpuSettings[] = { ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, true, true), ReportedConfigSetting("FuncReplacements", &g_Config.bFuncReplacements, true, true, true), ConfigSetting("HideSlowWarnings", &g_Config.bHideSlowWarnings, false, true, false), - ConfigSetting("HideStateWarnings", &g_Config.bHideStateWarnings, false, true, true), + ConfigSetting("HideStateWarnings", &g_Config.bHideStateWarnings, false, true, false), ConfigSetting("PreloadFunctions", &g_Config.bPreloadFunctions, false, true, true), ReportedConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0, true, true), From 06340bfa9c92d0ad8423ca3cacdd84795f71094a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 20 Jun 2018 03:05:27 -0400 Subject: [PATCH 254/301] Vulkan: Workaround Adreno discard bug. (#11197) This also explicitly enables early fragment tests when possible. Using conversative depth still works on Adreno and should allow some depth optimizations. --- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 15 ++++++++++++++- GPU/Vulkan/StencilBufferVulkan.cpp | 6 ++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index ab919a0cad..a8d3100098 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -36,7 +36,9 @@ static const char *vulkan_glsl_preamble = "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" - "#extension GL_ARB_shading_language_420pack : enable\n\n"; + "#extension GL_ARB_shading_language_420pack : enable\n" + "#extension GL_ARB_conservative_depth : enable\n" + "#extension GL_ARB_shader_image_load_store : enable\n\n"; #define WRITE p+=sprintf @@ -80,6 +82,13 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { bool isModeClear = id.Bit(FS_BIT_CLEARMODE); const char *shading = doFlatShading ? "flat" : ""; + bool earlyFragmentTests = !enableAlphaTest && !enableColorTest && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); + + if (earlyFragmentTests) { + WRITE(p, "layout (early_fragment_tests) in;\n"); + } else { + WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n"); + } WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseUBO {\n%s} base;\n", ub_baseStr); if (doTexture) { @@ -570,6 +579,10 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " z = (1.0/65535.0) * floor(z * 65535.0);\n"); } WRITE(p, " gl_FragDepth = z;\n"); + } else if (!earlyFragmentTests) { + // Adreno (and possibly MESA/others) apply early frag tests even with discard in the shader. + // Writing depth prevents the bug, even with depth_unchanged specified. + WRITE(p, " gl_FragDepth = gl_FragCoord.z;\n"); } WRITE(p, "}\n"); diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index 7ce1e00fe5..0ec2195e79 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -32,15 +32,17 @@ static const char *stencil_fs = R"(#version 450 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable +#extension GL_ARB_conservative_depth : enable +layout (depth_unchanged) out float gl_FragDepth; layout (binding = 0) uniform sampler2D tex; -layout(push_constant) uniform params { +layout (push_constant) uniform params { int u_stencilValue; }; layout (location = 0) in vec2 v_texcoord0; layout (location = 0) out vec4 fragColor0; void main() { - gl_FragDepth = 0.0; + gl_FragDepth = gl_FragCoord.z; if (u_stencilValue == 0) { fragColor0 = vec4(0.0); From fd8a0612fa3d22177cce0abd435dde799d69c35c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 22 Jun 2018 21:25:07 -0700 Subject: [PATCH 255/301] SaveState: Initialize some memory that is saved. At the very least, will help them compress better. Also good not to leak random memory. --- Common/ChunkFile.cpp | 4 ++-- Common/FixedSizeQueue.h | 2 ++ Core/HLE/__sceAudio.cpp | 1 + Core/HLE/sceKernelInterrupt.cpp | 1 + Core/HLE/sceKernelThread.cpp | 4 +++- Core/HW/SasAudio.h | 22 +++++++++++----------- Core/Util/BlockAllocator.cpp | 5 +++++ GPU/GPUCommon.h | 4 ++-- 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Common/ChunkFile.cpp b/Common/ChunkFile.cpp index cbc8dc1808..4e2334e735 100644 --- a/Common/ChunkFile.cpp +++ b/Common/ChunkFile.cpp @@ -300,7 +300,7 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, } // Create header - SChunkHeader header; + SChunkHeader header{}; header.Compress = compressed_buffer ? 1 : 0; header.Revision = REVISION_CURRENT; header.ExpectedSize = (u32)write_len; @@ -308,7 +308,7 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const std::string &filename, truncate_cpy(header.GitVersion, gitVersion); // Setup the fixed-length title. - char titleFixed[128]; + char titleFixed[128]{}; truncate_cpy(titleFixed, title.c_str()); // Now let's start writing out the file... diff --git a/Common/FixedSizeQueue.h b/Common/FixedSizeQueue.h index 60532cc693..d59555f7d8 100644 --- a/Common/FixedSizeQueue.h +++ b/Common/FixedSizeQueue.h @@ -46,6 +46,8 @@ public: head_ = 0; tail_ = 0; count_ = 0; + // Not entirely necessary, but keeps things clean. + memset(storage_, 0, sizeof(T) * N); } void push(T t) { diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 09c303fa95..99f40e4551 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -160,6 +160,7 @@ void __AudioDoState(PointerWrap &p) { p.Do(mixFrequency); + // TODO: This never happens because maxVer=1. if (s >= 2) { resampler.DoState(p); } else { diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 29c0cc5c7f..9513d2d019 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -317,6 +317,7 @@ void InterruptState::restore() void InterruptState::clear() { + savedCpu.reset(); } // http://forums.ps2dev.org/viewtopic.php?t=5687 diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 576c3f1182..28cf1b7442 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -544,7 +544,7 @@ public: p.Do(currentMipscallId); p.Do(currentCallbackId); - // TODO: How do I "version" adding a DoState method to ThreadContext? + // TODO: If we want to "version" a DoState method here, we can just use minVer = 0. p.Do(context); if (s <= 3) @@ -1824,6 +1824,8 @@ void ThreadContext::reset() fcr31 = 0x00000e00; hi = 0xDEADBEEF; lo = 0xDEADBEEF; + // Just for a clean state. + other[5] = 0; } void __KernelResetThread(Thread *t, int lowestPriority) diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index 59e47c76d1..480250dc70 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -111,21 +111,21 @@ public: private: s16 samples[28]; - int curSample; + int curSample = 0; - u32 data_; - u32 read_; - int curBlock_; - int loopStartBlock_; - int numBlocks_; + u32 data_ = 0; + u32 read_ = 0; + int curBlock_ = -1; + int loopStartBlock_ = -1; + int numBlocks_ = 0; // rolling state. start at 0, should probably reset to 0 on loops? - int s_1; - int s_2; + int s_1 = 0; + int s_2 = 0; - bool loopEnabled_; - bool loopAtNextBlock_; - bool end_; + bool loopEnabled_ = false; + bool loopAtNextBlock_ = false; + bool end_ = false; }; class SasAtrac3 { diff --git a/Core/Util/BlockAllocator.cpp b/Core/Util/BlockAllocator.cpp index 6c14dfe2b1..02496432b1 100644 --- a/Core/Util/BlockAllocator.cpp +++ b/Core/Util/BlockAllocator.cpp @@ -501,5 +501,10 @@ void BlockAllocator::Block::DoState(PointerWrap &p) p.Do(start); p.Do(size); p.Do(taken); + // Since we use truncate_cpy, the empty space is not zeroed. Zero it now. + // This avoids saving uninitialized memory. + size_t tagLen = strlen(tag); + if (tagLen != sizeof(tag)) + memset(tag + tagLen, 0, sizeof(tag) - tagLen); p.DoArray(tag, sizeof(tag)); } diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 9d379000e1..36442d50de 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -319,8 +319,8 @@ protected: DisplayList *currentList; DisplayListQueue dlQueue; - bool interruptRunning; - GPURunState gpuState; + bool interruptRunning = false; + GPURunState gpuState = GPUSTATE_RUNNING; bool isbreak; u64 drawCompleteTicks; u64 busyTicks; From 15244b7679ecf886f66447c2cb92a6038f36c32b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 22 Jun 2018 21:26:45 -0700 Subject: [PATCH 256/301] CoreTiming: Reset CPU mhz on reset. Oops - this wasn't being reset, and stayed at the previous value. See #5530. --- Core/CoreTiming.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index b3f9a9587b..150f5599ea 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -34,6 +34,7 @@ #include "Core/Reporting.h" #include "Common/ChunkFile.h" +static const int initialHz = 222000000; int CPU_HZ = 222000000; // is this really necessary? @@ -211,6 +212,7 @@ void Init() lastGlobalTimeUs = 0; hasTsEvents = 0; mhzChangeCallbacks.clear(); + CPU_HZ = initialHz; } void Shutdown() From 2f2c561b03cdb72c152032da0f071fde0e81d28a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 22 Jun 2018 21:33:12 -0700 Subject: [PATCH 257/301] UI: Interpret "unthrottle" to mean leave stepping. --- UI/EmuScreen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e0d84e15f3..85807bd5fa 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -464,6 +464,9 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { switch (virtualKeyCode) { case VIRTKEY_UNTHROTTLE: + if (coreState == CORE_STEPPING) { + Core_EnableStepping(false); + } PSP_CoreParameter().unthrottle = true; break; From 0d4bd341e992be23359df66ad8d15f5697d593e1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 23 Jun 2018 10:14:36 -0700 Subject: [PATCH 258/301] Windows: Separate Pause and Break actions. It was confusing that "Pause" made PPSSPP appear to hang. This has the side effect of making "Break on Load" work outside Windows. --- Core/CoreParameter.h | 2 +- Core/PSPLoaders.cpp | 6 +++--- UI/EmuScreen.cpp | 2 +- Windows/Debugger/Debugger_Disasm.cpp | 7 +++---- Windows/MainWindowMenu.cpp | 25 ++++++++++++++++--------- Windows/WindowsHost.cpp | 1 - Windows/ppsspp.rc | 15 ++++++++------- Windows/resource.h | 7 ++++--- android/jni/TestRunner.cpp | 2 +- headless/Headless.cpp | 2 +- libretro/libretro.cpp | 2 +- 11 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index 9e6ec3ef18..2f71d4c10d 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -56,7 +56,7 @@ struct CoreParameter { std::string mountRoot; // If non-empty, and fileToStart is an ELF or PBP, mount this as host0: / umd0:. std::string errorString; - bool startPaused; + bool startBreak; bool printfEmuLog; // writes "emulator:" logging to stdout std::string *collectEmuLog; bool headLess; // Try to avoid messageboxes etc diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 7567d0b29c..ecd58d3141 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -260,7 +260,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { // TODO: We can't use the initial error_string pointer. bool success = __KernelLoadExec(bootpath.c_str(), 0, &PSP_CoreParameter().errorString); if (success && coreState == CORE_POWERUP) { - coreState = PSP_CoreParameter().startPaused ? CORE_STEPPING : CORE_RUNNING; + coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING; } else { coreState = CORE_ERROR; // TODO: This is a crummy way to communicate the error... @@ -375,7 +375,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { std::thread th([finalName] { bool success = __KernelLoadExec(finalName.c_str(), 0, &PSP_CoreParameter().errorString); if (success && coreState == CORE_POWERUP) { - coreState = PSP_CoreParameter().startPaused ? CORE_STEPPING : CORE_RUNNING; + coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING; } else { coreState = CORE_ERROR; // TODO: This is a crummy way to communicate the error... @@ -393,7 +393,7 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { std::thread th([] { bool success = __KernelLoadGEDump("disc0:/data.ppdmp", &PSP_CoreParameter().errorString); if (success && coreState == CORE_POWERUP) { - coreState = PSP_CoreParameter().startPaused ? CORE_STEPPING : CORE_RUNNING; + coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING; } else { coreState = CORE_ERROR; // TODO: This is a crummy way to communicate the error... diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 85807bd5fa..9b8317ee4e 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -224,7 +224,7 @@ void EmuScreen::bootGame(const std::string &filename) { coreParam.fileToStart = filename; coreParam.mountIso = ""; coreParam.mountRoot = ""; - coreParam.startPaused = false; + coreParam.startBreak = !g_Config.bAutoRun; coreParam.printfEmuLog = false; coreParam.headLess = false; diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index bf5f9c7995..86f3645f1a 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -393,10 +393,9 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { CtrlDisAsmView *ptr = CtrlDisAsmView::getFrom(GetDlgItem(m_hDlg,IDC_DISASMVIEW)); CtrlRegisterList *reglist = CtrlRegisterList::getFrom(GetDlgItem(m_hDlg,IDC_REGLIST)); - switch(LOWORD(wParam)) - { - case ID_TOGGLE_PAUSE: - SendMessage(MainWindow::GetHWND(),WM_COMMAND,ID_TOGGLE_PAUSE,0); + switch (LOWORD(wParam)) { + case ID_TOGGLE_BREAK: + SendMessage(MainWindow::GetHWND(), WM_COMMAND, ID_TOGGLE_BREAK, 0); break; case ID_DEBUG_DISPLAYMEMVIEW: diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 051d9aa521..280d4bf136 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -60,10 +60,11 @@ namespace MainWindow { EnableMenuItem(menu, ID_FILE_LOADSTATEFILE, menuEnable); EnableMenuItem(menu, ID_FILE_QUICKSAVESTATE, menuEnable); EnableMenuItem(menu, ID_FILE_QUICKLOADSTATE, menuEnable); - EnableMenuItem(menu, ID_TOGGLE_PAUSE, menuEnable); + EnableMenuItem(menu, ID_EMULATION_PAUSE, menuEnable); EnableMenuItem(menu, ID_EMULATION_STOP, menuEnable); EnableMenuItem(menu, ID_EMULATION_RESET, menuEnable); EnableMenuItem(menu, ID_EMULATION_SWITCH_UMD, umdSwitchEnable); + EnableMenuItem(menu, ID_TOGGLE_BREAK, menuEnable); EnableMenuItem(menu, ID_DEBUG_LOADMAPFILE, menuEnable); EnableMenuItem(menu, ID_DEBUG_SAVEMAPFILE, menuEnable); EnableMenuItem(menu, ID_DEBUG_LOADSYMFILE, menuEnable); @@ -270,7 +271,7 @@ namespace MainWindow { TranslateMenuItem(menu, ID_FILE_EXIT, L"\tAlt+F4"); // Emulation menu - TranslateMenuItem(menu, ID_TOGGLE_PAUSE, L"\tF8", "Pause"); + TranslateMenuItem(menu, ID_EMULATION_PAUSE); TranslateMenuItem(menu, ID_EMULATION_STOP, L"\tCtrl+W"); TranslateMenuItem(menu, ID_EMULATION_RESET, L"\tCtrl+B"); TranslateMenuItem(menu, ID_EMULATION_SWITCH_UMD, L"\tCtrl+U", "Switch UMD"); @@ -281,16 +282,17 @@ namespace MainWindow { TranslateMenuItem(menu, ID_EMULATION_ROTATION_V_R); // Debug menu + TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", "Break"); + TranslateMenuItem(menu, ID_DEBUG_BREAKONLOAD); + TranslateMenuItem(menu, ID_DEBUG_IGNOREILLEGALREADS); TranslateMenuItem(menu, ID_DEBUG_LOADMAPFILE); TranslateMenuItem(menu, ID_DEBUG_SAVEMAPFILE); TranslateMenuItem(menu, ID_DEBUG_LOADSYMFILE); TranslateMenuItem(menu, ID_DEBUG_SAVESYMFILE); TranslateMenuItem(menu, ID_DEBUG_RESETSYMBOLTABLE); - TranslateMenuItem(menu, ID_DEBUG_DUMPNEXTFRAME); TranslateMenuItem(menu, ID_DEBUG_TAKESCREENSHOT, L"\tF12"); + TranslateMenuItem(menu, ID_DEBUG_DUMPNEXTFRAME); TranslateMenuItem(menu, ID_DEBUG_SHOWDEBUGSTATISTICS); - TranslateMenuItem(menu, ID_DEBUG_IGNOREILLEGALREADS); - TranslateMenuItem(menu, ID_DEBUG_RUNONLOAD); TranslateMenuItem(menu, ID_DEBUG_DISASSEMBLY, L"\tCtrl+D"); TranslateMenuItem(menu, ID_DEBUG_GEDEBUGGER, L"\tCtrl+G"); TranslateMenuItem(menu, ID_DEBUG_EXTRACTFILE); @@ -582,7 +584,7 @@ namespace MainWindow { ShellExecute(NULL, L"open", ConvertUTF8ToWString(g_Config.memStickDirectory).c_str(), 0, 0, SW_SHOW); break; - case ID_TOGGLE_PAUSE: + case ID_TOGGLE_BREAK: if (GetUIState() == UISTATE_PAUSEMENU) { // Causes hang //NativeMessageReceived("run", ""); @@ -603,6 +605,11 @@ namespace MainWindow { noFocusPause = !noFocusPause; // If we pause, override pause on lost focus break; + case ID_EMULATION_PAUSE: + NativeMessageReceived("pause", ""); + Core_EnableStepping(false); + break; + case ID_EMULATION_STOP: if (Core_IsStepping()) Core_EnableStepping(false); @@ -821,7 +828,7 @@ namespace MainWindow { PostMessage(hWnd, WM_CLOSE, 0, 0); break; - case ID_DEBUG_RUNONLOAD: + case ID_DEBUG_BREAKONLOAD: g_Config.bAutoRun = !g_Config.bAutoRun; break; @@ -1055,7 +1062,7 @@ namespace MainWindow { CHECKITEM(ID_DEBUG_IGNOREILLEGALREADS, g_Config.bIgnoreBadMemAccess); CHECKITEM(ID_DEBUG_SHOWDEBUGSTATISTICS, g_Config.bShowDebugStats); CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform); - CHECKITEM(ID_DEBUG_RUNONLOAD, g_Config.bAutoRun); + CHECKITEM(ID_DEBUG_BREAKONLOAD, !g_Config.bAutoRun); CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache); CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.iShowFPSCounter); CHECKITEM(ID_OPTIONS_FRAMESKIP_AUTO, g_Config.bAutoFrameSkip); @@ -1322,7 +1329,7 @@ namespace MainWindow { lastGlobalUIState = GetUIState(); bool isPaused = Core_IsStepping() && GetUIState() == UISTATE_INGAME; - TranslateMenuItem(menu, ID_TOGGLE_PAUSE, L"\tF8", isPaused ? "Run" : "Pause"); + TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", isPaused ? "Run" : "Break"); } // Message handler for about box. diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index ce306cbfe0..168f87c902 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -244,7 +244,6 @@ void WindowsHost::BootDone() { PostMessage(mainWindow_, WM_USER + 1, 0, 0); SetDebugMode(!g_Config.bAutoRun); - Core_EnableStepping(!g_Config.bAutoRun); } static std::string SymbolMapFilename(const char *currentFilename, const char* ext) { diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 466752e4df..19bff9a319 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -79,7 +79,7 @@ BEGIN "1", ID_OPTIONS_RESOLUTIONDUMMY, VIRTKEY, CONTROL, NOINVERT VK_F7, ID_OPTIONS_FRAMESKIPDUMMY, VIRTKEY, NOINVERT VK_F12, ID_DEBUG_TAKESCREENSHOT, VIRTKEY, NOINVERT - VK_F8, ID_TOGGLE_PAUSE, VIRTKEY, NOINVERT + VK_F8, ID_TOGGLE_BREAK, VIRTKEY, NOINVERT VK_RETURN, ID_OPTIONS_FULLSCREEN, ALT, VIRTKEY, NOINVERT VK_F11, ID_OPTIONS_FULLSCREEN, VIRTKEY, NOINVERT END @@ -94,7 +94,7 @@ BEGIN "2", ID_DEBUG_DISPLAYBREAKPOINTLIST, VIRTKEY, CONTROL, NOINVERT "3", ID_DEBUG_DISPLAYTHREADLIST, VIRTKEY, CONTROL, NOINVERT "4", ID_DEBUG_DISPLAYSTACKFRAMELIST, VIRTKEY, CONTROL, NOINVERT - VK_F7, ID_TOGGLE_PAUSE, VIRTKEY, NOINVERT + VK_F7, ID_TOGGLE_BREAK, VIRTKEY, NOINVERT VK_F8, ID_DEBUG_STEPOUT, VIRTKEY, NOINVERT VK_F9, ID_DEBUG_RUNTOLINE, VIRTKEY, NOINVERT VK_F10, ID_DEBUG_STEPOVER, VIRTKEY, NOINVERT @@ -483,7 +483,7 @@ BEGIN POPUP "Emulation" BEGIN - MENUITEM "Run", ID_TOGGLE_PAUSE + MENUITEM "Pause", ID_EMULATION_PAUSE MENUITEM "Stop", ID_EMULATION_STOP MENUITEM "Reset", ID_EMULATION_RESET MENUITEM "Switch UMD", ID_EMULATION_SWITCH_UMD @@ -498,18 +498,19 @@ BEGIN POPUP "Debug" BEGIN + MENUITEM "Run", ID_TOGGLE_BREAK + MENUITEM "Break on Load", ID_DEBUG_BREAKONLOAD + MENUITEM "Ignore Illegal Reads/Writes", ID_DEBUG_IGNOREILLEGALREADS + MENUITEM SEPARATOR MENUITEM "Load Map File...", ID_DEBUG_LOADMAPFILE MENUITEM "Save Map File...", ID_DEBUG_SAVEMAPFILE MENUITEM "Load .sym File...", ID_DEBUG_LOADSYMFILE MENUITEM "Save .sym File...", ID_DEBUG_SAVESYMFILE MENUITEM "Reset Symbol Table", ID_DEBUG_RESETSYMBOLTABLE MENUITEM SEPARATOR - MENUITEM "Dump Next Frame to Log", ID_DEBUG_DUMPNEXTFRAME MENUITEM "Take Screenshot", ID_DEBUG_TAKESCREENSHOT - MENUITEM SEPARATOR + MENUITEM "Dump Next Frame to Log", ID_DEBUG_DUMPNEXTFRAME MENUITEM "Show Debug Statistics", ID_DEBUG_SHOWDEBUGSTATISTICS - MENUITEM "Ignore Illegal Reads/Writes", ID_DEBUG_IGNOREILLEGALREADS - MENUITEM "Run on Load", ID_DEBUG_RUNONLOAD MENUITEM SEPARATOR MENUITEM "Disassembly", ID_DEBUG_DISASSEMBLY MENUITEM "GE Debugger...", ID_DEBUG_GEDEBUGGER diff --git a/Windows/resource.h b/Windows/resource.h index ca52341e45..06b23bde50 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -176,7 +176,7 @@ #define ID_DISASM_COPYINSTRUCTIONDISASM 40006 #define ID_DISASM_COPYINSTRUCTIONHEX 40007 #define ID_EMULATION_SPEEDLIMIT 40008 -#define ID_TOGGLE_PAUSE 40009 +#define ID_TOGGLE_BREAK 40009 #define ID_EMULATION_STOP 40010 #define ID_FILE_LOAD 40011 #define ID_HELP_ABOUT 40012 @@ -205,7 +205,7 @@ #define ID_FILE_QUICKSAVESTATE_HC 40036 #define ID_FILE_QUICKLOADSTATE_HC 40037 #define ID_OPTIONS_CONTROLS 40038 -#define ID_DEBUG_RUNONLOAD 40039 +#define ID_DEBUG_BREAKONLOAD 40039 #define ID_DEBUG_DUMPNEXTFRAME 40040 #define ID_OPTIONS_VERTEXCACHE 40041 #define ID_OPTIONS_SHOWFPS 40042 @@ -339,6 +339,7 @@ #define ID_OPTIONS_WINDOW8X 40174 #define ID_OPTIONS_WINDOW9X 40175 #define ID_OPTIONS_WINDOW10X 40176 +#define ID_EMULATION_PAUSE 40177 // Dummy option to let the buffered rendering hotkey cycle through all the options. #define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500 @@ -351,7 +352,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 256 -#define _APS_NEXT_COMMAND_VALUE 40177 +#define _APS_NEXT_COMMAND_VALUE 40178 #define _APS_NEXT_CONTROL_VALUE 1200 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/android/jni/TestRunner.cpp b/android/jni/TestRunner.cpp index c58aa8e9e4..2a24811289 100644 --- a/android/jni/TestRunner.cpp +++ b/android/jni/TestRunner.cpp @@ -92,7 +92,7 @@ bool RunTests() { coreParam.graphicsContext = nullptr; coreParam.mountIso = ""; coreParam.mountRoot = baseDirectory + "pspautotests/"; - coreParam.startPaused = false; + coreParam.startBreak = false; coreParam.printfEmuLog = false; coreParam.headLess = true; coreParam.renderWidth = 480; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index da101c27ce..8edea02384 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -333,7 +333,7 @@ int main(int argc, const char* argv[]) coreParameter.enableSound = false; coreParameter.mountIso = mountIso ? mountIso : ""; coreParameter.mountRoot = mountRoot ? mountRoot : ""; - coreParameter.startPaused = false; + coreParameter.startBreak = false; coreParameter.printfEmuLog = !autoCompare; coreParameter.headLess = true; coreParameter.renderWidth = 480; diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 0759ffbd97..ace31cefaf 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -549,7 +549,7 @@ bool retro_load_game(const struct retro_game_info *game) { coreParam.enableSound = true; coreParam.fileToStart = std::string(game->path); coreParam.mountIso = ""; - coreParam.startPaused = false; + coreParam.startBreak = false; coreParam.printfEmuLog = true; coreParam.headLess = true; coreParam.unthrottle = true; From 265611cff510c2857a40178eb645d3b159d27905 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 23 Jun 2018 10:54:40 -0700 Subject: [PATCH 259/301] Debugger: Use Break instead of Stop. It does not stop emulation. --- Windows/Debugger/Debugger_Disasm.cpp | 2 +- Windows/ppsspp.rc | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 86f3645f1a..4a2e54ee9d 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -841,7 +841,7 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC) if (GetUIState() == UISTATE_INGAME && PSP_IsInited()) { - SetDlgItemText(m_hDlg, IDC_STOPGO, L"Stop"); + SetDlgItemText(m_hDlg, IDC_STOPGO, L"Break"); EnableWindow(GetDlgItem(hDlg, IDC_STOPGO), TRUE); } else diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 19bff9a319..3099c3e3f9 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -179,13 +179,13 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN LTEXT "Ctr:",IDC_STATIC,6,2,12,8 EDITTEXT IDC_DEBUG_COUNT,30,2,39,12,ES_UPPERCASE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER - PUSHBUTTON "Stop",IDC_STOPGO,111,0,24,14 - PUSHBUTTON "Step Into",IDC_STEP,143,0,39,14 - PUSHBUTTON "Step Over",IDC_STEPOVER,183,0,40,14 - PUSHBUTTON "Step Out",IDC_STEPOUT,224,0,40,14 - PUSHBUTTON "Next HLE",IDC_STEPHLE,272,0,40,14 - PUSHBUTTON "Breakpoint",IDC_MEMCHECK,313,0,45,14 - EDITTEXT IDC_THREADNAME,366,3,150,10,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + PUSHBUTTON "Break",IDC_STOPGO,111,0,28,14 + PUSHBUTTON "Step Into",IDC_STEP,147,0,39,14 + PUSHBUTTON "Step Over",IDC_STEPOVER,187,0,40,14 + PUSHBUTTON "Step Out",IDC_STEPOUT,228,0,40,14 + PUSHBUTTON "Next HLE",IDC_STEPHLE,276,0,40,14 + PUSHBUTTON "Breakpoint",IDC_MEMCHECK,317,0,45,14 + EDITTEXT IDC_THREADNAME,370,3,150,10,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER EDITTEXT IDC_ADDRESS,11,24,91,13,ES_AUTOHSCROLL | ES_WANTRETURN PUSHBUTTON "PC",IDC_GOTOPC,11,40,15,13 PUSHBUTTON "RA",IDC_GOTOLR,28,40,14,13 From dc840c077d7fe846c15acc8c2a7a5297d372be44 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 18:22:34 -0700 Subject: [PATCH 260/301] UI: Add a virtkey for holding alt speed. Can be used for slow motion or fast motion (esp. if unthrottle is too fast.) --- Common/KeyMap.cpp | 1 + Common/KeyMap.h | 1 + UI/EmuScreen.cpp | 19 +++++++++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index ffe76c87a0..805bd4b4d2 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -645,6 +645,7 @@ const KeyMap_IntStrPair psp_button_names[] = { {VIRTKEY_RAPID_FIRE, "RapidFire"}, {VIRTKEY_UNTHROTTLE, "Unthrottle"}, {VIRTKEY_SPEED_TOGGLE, "SpeedToggle"}, + {VIRTKEY_SPEED_PRESS, "Speed toggle hold"}, {VIRTKEY_PAUSE, "Pause"}, #ifndef MOBILE_DEVICE {VIRTKEY_FRAME_ADVANCE, "Frame Advance"}, diff --git a/Common/KeyMap.h b/Common/KeyMap.h index fd56d4d8f5..810beec448 100644 --- a/Common/KeyMap.h +++ b/Common/KeyMap.h @@ -52,6 +52,7 @@ enum { VIRTKEY_DEVMENU = 0x40000014, VIRTKEY_FRAME_ADVANCE = 0x40000015, VIRTKEY_RECORD = 0x40000016, + VIRTKEY_SPEED_PRESS = 0x40000017, VIRTKEY_LAST, VIRTKEY_COUNT = VIRTKEY_LAST - VIRTKEY_FIRST }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 85807bd5fa..cf268c5ba6 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -474,13 +474,19 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { if (PSP_CoreParameter().fpsLimit == 0) { PSP_CoreParameter().fpsLimit = 1; osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); - } - else if (PSP_CoreParameter().fpsLimit == 1) { + } else if (PSP_CoreParameter().fpsLimit == 1) { PSP_CoreParameter().fpsLimit = 0; osm.Show(sc->T("standard", "Speed: standard"), 1.0); } break; + case VIRTKEY_SPEED_PRESS: + if (PSP_CoreParameter().fpsLimit == 0) { + PSP_CoreParameter().fpsLimit = 1; + osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); + } + break; + case VIRTKEY_PAUSE: pauseTrigger_ = true; break; @@ -580,11 +586,20 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { } void EmuScreen::onVKeyUp(int virtualKeyCode) { + I18NCategory *sc = GetI18NCategory("Screen"); + switch (virtualKeyCode) { case VIRTKEY_UNTHROTTLE: PSP_CoreParameter().unthrottle = false; break; + case VIRTKEY_SPEED_PRESS: + if (PSP_CoreParameter().fpsLimit == 1) { + PSP_CoreParameter().fpsLimit = 0; + osm.Show(sc->T("standard", "Speed: standard"), 1.0); + } + break; + case VIRTKEY_AXIS_X_MIN: case VIRTKEY_AXIS_X_MAX: setVKeyAnalogX(CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); From b4496f197548c30766b2106d7ad4f062293360e9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 18:42:31 -0700 Subject: [PATCH 261/301] Core: Move config enums to separate file. These are a bit strewn about and there are constants that aren't consistently used, which just adds confusion. --- CMakeLists.txt | 1 + Core/Config.cpp | 5 ++ Core/Config.h | 50 +------------------- Core/ConfigValues.h | 67 +++++++++++++++++++++++++++ Core/Core.vcxproj | 1 + Core/Core.vcxproj.filters | 3 ++ Core/HLE/sceIo.cpp | 1 + Core/HW/StereoResampler.cpp | 1 + Core/MIPS/MIPS.cpp | 1 + Core/MemMap.cpp | 1 + Core/MemMapFunctions.cpp | 1 + Core/PSPLoaders.cpp | 1 + GPU/Common/FramebufferCommon.cpp | 1 + GPU/Common/GPUStateUtils.cpp | 1 + GPU/Common/VertexDecoderCommon.cpp | 1 + GPU/D3D11/FramebufferManagerD3D11.cpp | 1 + GPU/Directx9/FramebufferDX9.cpp | 1 + GPU/Directx9/GPU_DX9.cpp | 1 + GPU/GLES/FramebufferManagerGLES.cpp | 1 + GPU/GLES/StencilBufferGLES.cpp | 2 + GPU/Software/SoftGpu.cpp | 1 + GPU/Vulkan/FramebufferVulkan.cpp | 1 + Qt/QtMain.cpp | 2 + Qt/QtMain.h | 1 + SDL/SDLGLGraphicsContext.cpp | 1 + SDL/SDLMain.cpp | 1 + SDL/SDLVulkanGraphicsContext.cpp | 1 + UI/DevScreens.cpp | 1 + UI/DisplayLayoutScreen.cpp | 1 + UI/EmuScreen.cpp | 1 + UI/GameSettingsScreen.cpp | 1 + UI/NativeApp.cpp | 1 + Windows/DSoundStream.cpp | 1 + Windows/DSoundStream.h | 1 + Windows/EmuThread.cpp | 1 + Windows/GPU/D3D11Context.cpp | 1 + Windows/GPU/D3D9Context.cpp | 1 + Windows/GPU/WindowsGLContext.cpp | 1 + Windows/GPU/WindowsVulkanContext.cpp | 1 + Windows/MainWindow.cpp | 1 + Windows/MainWindowMenu.cpp | 1 + Windows/WindowsHost.cpp | 1 + Windows/main.cpp | 1 + android/jni/AndroidEGLContext.cpp | 1 + android/jni/AndroidJavaGLContext.cpp | 1 + android/jni/AndroidVulkanContext.cpp | 1 + android/jni/app-android.cpp | 1 + headless/Headless.cpp | 1 + headless/SDLHeadlessHost.cpp | 1 + ios/ViewController.mm | 1 + libretro/LibretroGLContext.cpp | 1 + libretro/LibretroVulkanContext.cpp | 1 + libretro/libretro.cpp | 1 + unittest/JitHarness.cpp | 1 + unittest/TestVertexJit.cpp | 1 + 55 files changed, 129 insertions(+), 49 deletions(-) create mode 100644 Core/ConfigValues.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 386f4e6eaf..8ae5fa9024 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1381,6 +1381,7 @@ add_library(${CoreLibName} ${CoreLinkType} ${CoreExtra} Core/Config.cpp Core/Config.h + Core/ConfigValues.h Core/Core.cpp Core/Core.h Core/Compatibility.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index 2751ae147f..3b719b6992 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -38,6 +38,7 @@ #include "Common/StringUtils.h" #include "Common/Vulkan/VulkanLoader.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Loaders.h" #include "Core/HLE/sceUtility.h" #include "GPU/Common/FramebufferCommon.h" @@ -1481,3 +1482,7 @@ void Config::GetReportingInfo(UrlEncoder &data) { } } } + +bool Config::IsPortrait() const { + return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && iRenderingMode != 0; +} diff --git a/Core/Config.h b/Core/Config.h index 978b15614f..f6b780269b 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -25,52 +25,6 @@ extern const char *PPSSPP_GIT_VERSION; -const int PSP_MODEL_FAT = 0; -const int PSP_MODEL_SLIM = 1; -const int PSP_DEFAULT_FIRMWARE = 660; -static const s8 VOLUME_OFF = 0; -static const s8 VOLUME_MAX = 10; - -enum class CPUCore { - INTERPRETER = 0, - JIT = 1, - IR_JIT = 2, -}; - -enum { - ROTATION_AUTO = 0, - ROTATION_LOCKED_HORIZONTAL = 1, - ROTATION_LOCKED_VERTICAL = 2, - ROTATION_LOCKED_HORIZONTAL180 = 3, - ROTATION_LOCKED_VERTICAL180 = 4, -}; - -enum BufferFilter { - SCALE_LINEAR = 1, - SCALE_NEAREST = 2, -}; - -// Software is not among these because it will have one of these perform the blit to display. -enum class GPUBackend { - OPENGL = 0, - DIRECT3D9 = 1, - DIRECT3D11 = 2, - VULKAN = 3, -}; - -enum AudioBackendType { - AUDIO_BACKEND_AUTO, - AUDIO_BACKEND_DSOUND, - AUDIO_BACKEND_WASAPI, -}; - -// For iIOTimingMethod. -enum IOTimingMethods { - IOTIMING_FAST = 0, - IOTIMING_HOST = 1, - IOTIMING_REALISTIC = 2, -}; - namespace http { class Download; class Downloader; @@ -510,9 +464,7 @@ public: void GetReportingInfo(UrlEncoder &data); - bool IsPortrait() const { - return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && iRenderingMode != 0; - } + bool IsPortrait() const; protected: void LoadStandardControllerIni(); diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h new file mode 100644 index 0000000000..8b31c957e5 --- /dev/null +++ b/Core/ConfigValues.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include + +const int PSP_MODEL_FAT = 0; +const int PSP_MODEL_SLIM = 1; +const int PSP_DEFAULT_FIRMWARE = 660; +static const int8_t VOLUME_OFF = 0; +static const int8_t VOLUME_MAX = 10; + +enum class CPUCore { + INTERPRETER = 0, + JIT = 1, + IR_JIT = 2, +}; + +enum { + ROTATION_AUTO = 0, + ROTATION_LOCKED_HORIZONTAL = 1, + ROTATION_LOCKED_VERTICAL = 2, + ROTATION_LOCKED_HORIZONTAL180 = 3, + ROTATION_LOCKED_VERTICAL180 = 4, +}; + +enum BufferFilter { + SCALE_LINEAR = 1, + SCALE_NEAREST = 2, +}; + +// Software is not among these because it will have one of these perform the blit to display. +enum class GPUBackend { + OPENGL = 0, + DIRECT3D9 = 1, + DIRECT3D11 = 2, + VULKAN = 3, +}; + +enum AudioBackendType { + AUDIO_BACKEND_AUTO, + AUDIO_BACKEND_DSOUND, + AUDIO_BACKEND_WASAPI, +}; + +// For iIOTimingMethod. +enum IOTimingMethods { + IOTIMING_FAST = 0, + IOTIMING_HOST = 1, + IOTIMING_REALISTIC = 2, +}; + diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index a8765dac4c..c0751c917a 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -544,6 +544,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 6e86aea5b2..b398758157 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1352,6 +1352,9 @@ Debugger\WebSocket + + Core + diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 2436c69bd4..dc85a0816b 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -24,6 +24,7 @@ #include "Core/Core.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Debugger/Breakpoints.h" #include "Core/ELF/ParamSFO.h" #include "Core/MemMapHelpers.h" diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index ee636a89fe..8b2fcfadd1 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -37,6 +37,7 @@ #include "Common/MathUtil.h" #include "Common/Atomics.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/HW/StereoResampler.h" #include "Core/HLE/__sceAudio.h" #include "Core/Util/AudioFormat.h" // for clamp_u8 diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 37f16025ad..a04e8f2387 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -22,6 +22,7 @@ #include "Common.h" #include "Common/ChunkFile.h" +#include "Core/ConfigValues.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSInt.h" #include "Core/MIPS/MIPSTables.h" diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 0fc1355ae0..9c7a2cce19 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -38,6 +38,7 @@ #include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/HLE/ReplaceTables.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index bf2cff521d..a463a0e0ae 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -21,6 +21,7 @@ #include "Core/Core.h" #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Host.h" #include "Core/Reporting.h" diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 7567d0b29c..ea322af435 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -48,6 +48,7 @@ #include "Host.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/PSPLoaders.h" #include "Core/HLE/HLE.h" diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 928f2751ee..7b99614947 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -28,6 +28,7 @@ #include "Common/ColorConv.h" #include "Common/Common.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/CoreParameter.h" #include "Core/Host.h" #include "Core/Reporting.h" diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index d1091b9a3e..4f2b4dd3bd 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -22,6 +22,7 @@ #include "Common/StringUtils.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "GPU/ge_constants.h" diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 7d40fe5293..0b8ee6736c 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -24,6 +24,7 @@ #include "Common/CPUDetect.h" #include "Common/ColorConv.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/MemMap.h" #include "Core/HDRemaster.h" #include "Core/Reporting.h" diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 0bfc412541..f2910cbec6 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -31,6 +31,7 @@ #include "Core/Host.h" #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/Reporting.h" #include "GPU/ge_constants.h" diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 8b6025732a..a66faf39d1 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -22,6 +22,7 @@ #include "Core/Host.h" #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/Reporting.h" #include "GPU/ge_constants.h" diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index adb50ecbf0..24aa119525 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -28,6 +28,7 @@ #include "Core/MIPS/MIPS.h" #include "Core/Host.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Reporting.h" #include "Core/System.h" diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index e15aa36983..81b2498346 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -32,6 +32,7 @@ #include "Core/Host.h" #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/Reporting.h" #include "GPU/ge_constants.h" diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 9dac4c9a3f..d8effd95ae 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -16,6 +16,8 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "gfx_es2/glsl_program.h" +#include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Reporting.h" #include "GPU/Common/StencilCommon.h" #include "GPU/GLES/FramebufferManagerGLES.h" diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 9432ba97ef..5d3b20c8e3 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -21,6 +21,7 @@ #include "Common/ColorConv.h" #include "Common/GraphicsContext.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Host.h" #include "Core/MemMap.h" diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index d44b2a9983..9665019ffa 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -35,6 +35,7 @@ #include "Core/Host.h" #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/Reporting.h" #include "Core/HLE/sceDisplay.h" diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 304d164ab0..ada30411ad 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -31,6 +31,8 @@ #include "gfx_es2/gpu_features.h" #include "math/math_util.h" #include "thread/threadutil.h" +#include "Core/Config.h" +#include "Core/ConfigValues.h" #include diff --git a/Qt/QtMain.h b/Qt/QtMain.h index 958e139bcc..58fefb6cfe 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -38,6 +38,7 @@ QTM_USE_NAMESPACE #include "Common/GraphicsContext.h" #include "Core/Core.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "thin3d/thin3d_create.h" #include "thin3d/GLRenderManager.h" diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index e3dc209e3f..42ed9a261b 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -1,6 +1,7 @@ #include #include "SDLGLGraphicsContext.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "base/NativeApp.h" #include "base/display.h" diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 25f23e37a2..85b49825a1 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -51,6 +51,7 @@ SDLJoystick *joystick = NULL; #include "Core/System.h" #include "Core/Core.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Common/GraphicsContext.h" #include "SDLGLGraphicsContext.h" #include "SDLVulkanGraphicsContext.h" diff --git a/SDL/SDLVulkanGraphicsContext.cpp b/SDL/SDLVulkanGraphicsContext.cpp index 898d2c3a2a..a29d41ac3e 100644 --- a/SDL/SDLVulkanGraphicsContext.cpp +++ b/SDL/SDLVulkanGraphicsContext.cpp @@ -1,4 +1,5 @@ #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "base/NativeApp.h" #include "base/display.h" #include "thin3d/thin3d.h" diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 227265233b..12d1ee1e14 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -30,6 +30,7 @@ #include "Core/MemMap.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/CoreParameter.h" #include "Core/MIPS/MIPSTables.h" diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 4e719533ed..a41b2b53cf 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -26,6 +26,7 @@ #include "DisplayLayoutScreen.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "DisplayLayoutEditor.h" #include "GPU/Common/FramebufferCommon.h" diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index cf268c5ba6..8d4baa4f81 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -41,6 +41,7 @@ #include "Core/AVIDump.h" #endif #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/CoreTiming.h" #include "Core/CoreParameter.h" #include "Core/Core.h" diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 2834218006..f4e45b3012 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -49,6 +49,7 @@ #include "Common/FileUtil.h" #include "Common/OSVersion.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Host.h" #include "Core/System.h" #include "Core/Reporting.h" diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 2d9326969a..bd9c3937ee 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -71,6 +71,7 @@ #include "Common/MemArena.h" #include "Common/GraphicsContext.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Core.h" #include "Core/FileLoaders/DiskCachingFileLoader.h" #include "Core/Host.h" diff --git a/Windows/DSoundStream.cpp b/Windows/DSoundStream.cpp index 79d8d2699e..ca15e20856 100644 --- a/Windows/DSoundStream.cpp +++ b/Windows/DSoundStream.cpp @@ -12,6 +12,7 @@ #include "thread/threadutil.h" #include "Common/OSVersion.h" +#include "Core/ConfigValues.h" #include "Core/Reporting.h" #include "Core/Util/AudioFormat.h" #include "Windows/W32Util/Misc.h" diff --git a/Windows/DSoundStream.h b/Windows/DSoundStream.h index 95aee96326..fa101f4b71 100644 --- a/Windows/DSoundStream.h +++ b/Windows/DSoundStream.h @@ -2,6 +2,7 @@ #include "Common/CommonWindows.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" typedef int (*StreamCallback)(short *buffer, int numSamples, int bits, int rate, int channels); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 34cf5846e8..987bb8b5e4 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -22,6 +22,7 @@ #include "Core/Host.h" #include "Core/System.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "thread/threadutil.h" enum class EmuThreadState { diff --git a/Windows/GPU/D3D11Context.cpp b/Windows/GPU/D3D11Context.cpp index 7e6b748271..806458b1e8 100644 --- a/Windows/GPU/D3D11Context.cpp +++ b/Windows/GPU/D3D11Context.cpp @@ -10,6 +10,7 @@ #include "i18n/i18n.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Reporting.h" #include "Core/System.h" #include "Windows/GPU/D3D11Context.h" diff --git a/Windows/GPU/D3D9Context.cpp b/Windows/GPU/D3D9Context.cpp index ad71c0a48d..80933615b2 100644 --- a/Windows/GPU/D3D9Context.cpp +++ b/Windows/GPU/D3D9Context.cpp @@ -8,6 +8,7 @@ #include "i18n/i18n.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Reporting.h" #include "Core/System.h" #include "Common/OSVersion.h" diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index 7004fdde24..f3808583b0 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -26,6 +26,7 @@ #include "GL/gl.h" #include "GL/wglew.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Core.h" #include "util/text/utf8.h" #include "i18n/i18n.h" diff --git a/Windows/GPU/WindowsVulkanContext.cpp b/Windows/GPU/WindowsVulkanContext.cpp index e82b5bc92b..ad619ab339 100644 --- a/Windows/GPU/WindowsVulkanContext.cpp +++ b/Windows/GPU/WindowsVulkanContext.cpp @@ -51,6 +51,7 @@ #include #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Common/Vulkan/VulkanLoader.h" #include "Common/Vulkan/VulkanContext.h" diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 67a820042e..83b5286771 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -42,6 +42,7 @@ #include "Core/Core.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Debugger/SymbolMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 051d9aa521..1bf76c3a36 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -20,6 +20,7 @@ #include "GPU/Common/PostShader.h" #include "GPU/GLES/FramebufferManagerGLES.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/FileSystems/MetaFileSystem.h" #include "UI/OnScreenDisplay.h" #include "Windows/MainWindowMenu.h" diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index ce306cbfe0..e5421dc881 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -42,6 +42,7 @@ #include "Common/StringUtils.h" #include "Core/Core.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/CoreParameter.h" #include "Core/System.h" #include "Core/Debugger/SymbolMap.h" diff --git a/Windows/main.cpp b/Windows/main.cpp index a003e3739a..01c0fe6664 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -36,6 +36,7 @@ #include "net/resolve.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/SaveState.h" #include "Windows/EmuThread.h" #include "Windows/DSoundStream.h" diff --git a/android/jni/AndroidEGLContext.cpp b/android/jni/AndroidEGLContext.cpp index 3eb13c8689..34d21597f5 100644 --- a/android/jni/AndroidEGLContext.cpp +++ b/android/jni/AndroidEGLContext.cpp @@ -6,6 +6,7 @@ #include "AndroidEGLContext.h" #include "GL/GLInterface/EGLAndroid.h" +#include "Core/ConfigValues.h" #include "Core/System.h" bool AndroidEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) { diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp index 0f0937db08..aca9f8fcc9 100644 --- a/android/jni/AndroidJavaGLContext.cpp +++ b/android/jni/AndroidJavaGLContext.cpp @@ -3,6 +3,7 @@ #include "AndroidJavaGLContext.h" #include "base/NativeApp.h" #include "gfx_es2/gpu_features.h" +#include "Core/ConfigValues.h" #include "Core/System.h" AndroidJavaEGLGraphicsContext::AndroidJavaEGLGraphicsContext() { diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index 4240bde18c..128183dcc9 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -9,6 +9,7 @@ #include "thin3d/thin3d_create.h" #include "util/text/parsers.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" static VulkanLogOptions g_LogOptions; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 38fc3f6582..9206bcd59b 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -43,6 +43,7 @@ #include "AndroidJavaGLContext.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Loaders.h" #include "Core/System.h" #include "Common/CPUDetect.h" diff --git a/headless/Headless.cpp b/headless/Headless.cpp index da101c27ce..6f939a909a 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -11,6 +11,7 @@ #include "Common/FileUtil.h" #include "Common/GraphicsContext.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/System.h" diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index a2ec3a9b51..1545a53f9c 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -27,6 +27,7 @@ #include "Common/GraphicsContext.h" #include "Core/CoreParameter.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/GPUState.h" diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 2b36a65ada..3ca85ebcdf 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -23,6 +23,7 @@ #include "gfx_es2/gpu_features.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "Common/GraphicsContext.h" diff --git a/libretro/LibretroGLContext.cpp b/libretro/LibretroGLContext.cpp index c8a620c3cc..c8260650cb 100644 --- a/libretro/LibretroGLContext.cpp +++ b/libretro/LibretroGLContext.cpp @@ -1,6 +1,7 @@ #include "Common/Log.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "gfx_es2/gpu_features.h" diff --git a/libretro/LibretroVulkanContext.cpp b/libretro/LibretroVulkanContext.cpp index 7530fd6916..5cc7d4f09b 100644 --- a/libretro/LibretroVulkanContext.cpp +++ b/libretro/LibretroVulkanContext.cpp @@ -4,6 +4,7 @@ #include "Common/Vulkan/VulkanDebug.h" #include "Common/Log.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/System.h" #include "GPU/GPUInterface.h" #include "util/text/parsers.h" diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 0759ffbd97..1a7f9f15ae 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -8,6 +8,7 @@ #include "base/timeutil.h" #include "Common/ChunkFile.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "Core/Core.h" #include "Core/HLE/sceCtrl.h" #include "Core/HLE/sceUtility.h" diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index 29ab315e44..a34f3f4952 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -19,6 +19,7 @@ #include "base/timeutil.h" #include "base/NativeApp.h" +#include "Core/ConfigValues.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Core/MIPS/MIPSCodeUtils.h" diff --git a/unittest/TestVertexJit.cpp b/unittest/TestVertexJit.cpp index 40dd640c8b..75e5985888 100644 --- a/unittest/TestVertexJit.cpp +++ b/unittest/TestVertexJit.cpp @@ -18,6 +18,7 @@ #include "base/timeutil.h" #include "Common/Common.h" #include "Core/Config.h" +#include "Core/ConfigValues.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/ge_constants.h" #include "GPU/GPUState.h" From e2c217ab2998de26356724d281364f53c74e49c1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 19:20:23 -0700 Subject: [PATCH 262/301] Core: More consistently use config enums. --- Core/Config.cpp | 10 +++++----- Core/Config.h | 2 +- Core/ConfigValues.h | 6 ++++++ GPU/Common/FramebufferCommon.cpp | 10 +++++----- GPU/Common/ShaderUniforms.cpp | 5 +++-- GPU/GLES/GPU_GLES.cpp | 2 +- GPU/Software/Rasterizer.cpp | 5 +++-- GPU/Vulkan/DrawEngineVulkan.cpp | 8 ++++---- UI/DisplayLayoutScreen.cpp | 14 +++++++------- Windows/MainWindowMenu.cpp | 2 +- headless/Headless.cpp | 3 ++- 11 files changed, 38 insertions(+), 29 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 3b719b6992..cb4044d8d1 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -384,9 +384,9 @@ static ConfigSetting generalSettings[] = { ConfigSetting("RemoteDebuggerOnStartup", &g_Config.bRemoteDebuggerOnStartup, false), #ifdef __ANDROID__ - ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, 1), + ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, ROTATION_LOCKED_HORIZONTAL), #endif - ConfigSetting("InternalScreenRotation", &g_Config.iInternalScreenRotation, 1), + ConfigSetting("InternalScreenRotation", &g_Config.iInternalScreenRotation, ROTATION_LOCKED_HORIZONTAL), #if defined(USING_WIN_UI) ConfigSetting("TopMost", &g_Config.bTopMost, false), @@ -454,7 +454,7 @@ static bool DefaultFrameskipUnthrottle() { } static int DefaultZoomType() { - return 2; + return (int)SmallDisplayZoom::AUTO; } static bool DefaultTimerHack() { @@ -519,7 +519,7 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("HardwareTransform", &g_Config.bHardwareTransform, true, true, true), ReportedConfigSetting("SoftwareSkinning", &g_Config.bSoftwareSkinning, true, true, true), ReportedConfigSetting("TextureFiltering", &g_Config.iTexFiltering, 1, true, true), - ReportedConfigSetting("BufferFiltering", &g_Config.iBufFilter, 1, true, true), + ReportedConfigSetting("BufferFiltering", &g_Config.iBufFilter, SCALE_LINEAR, true, true), ReportedConfigSetting("InternalResolution", &g_Config.iInternalResolution, &DefaultInternalResolution, true, true), ReportedConfigSetting("AndroidHwScale", &g_Config.iAndroidHwScale, &DefaultAndroidHwScale), ReportedConfigSetting("HighQualityDepth", &g_Config.bHighQualityDepth, true, true, true), @@ -1484,5 +1484,5 @@ void Config::GetReportingInfo(UrlEncoder &data) { } bool Config::IsPortrait() const { - return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && iRenderingMode != 0; + return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && iRenderingMode != FB_NON_BUFFERED_MODE; } diff --git a/Core/Config.h b/Core/Config.h index f6b780269b..3898eba28e 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -157,7 +157,7 @@ public: bool bTrueColor; bool bReplaceTextures; bool bSaveNewTextures; - int iTexScalingLevel; // 1 = off, 2 = 2x, ..., 5 = 5x + int iTexScalingLevel; // 0 = auto, 1 = off, 2 = 2x, ..., 5 = 5x int iTexScalingType; // 0 = xBRZ, 1 = Hybrid bool bTexDeposterize; int iFpsLimit; diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 8b31c957e5..bc2df61f37 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -65,3 +65,9 @@ enum IOTimingMethods { IOTIMING_REALISTIC = 2, }; +enum class SmallDisplayZoom { + STRETCH = 0, + PARTIAL_STRETCH = 1, + AUTO = 2, + MANUAL = 3, +}; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 7b99614947..b8b1b7a48a 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -48,11 +48,11 @@ void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180; - if (g_Config.iSmallDisplayZoomType == 0) { // Stretching + if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::STRETCH) { outW = frameW; outH = frameH; } else { - if (g_Config.iSmallDisplayZoomType == 3) { // Manual Scaling + if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::MANUAL) { float offsetX = (g_Config.fSmallDisplayOffsetX - 0.5f) * 2.0f * frameW; float offsetY = (g_Config.fSmallDisplayOffsetY - 0.5f) * 2.0f * frameH; // Have to invert Y for GL @@ -75,7 +75,7 @@ void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW *h = floorf(smallDisplayW); return; } - } else if (g_Config.iSmallDisplayZoomType == 2) { // Auto Scaling + } else if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::AUTO) { // Stretch to 1080 for 272*4. But don't distort if not widescreen (i.e. ultrawide of halfwide.) float pixelCrop = frameH / 270.0f; float resCommonWidescreen = pixelCrop - floor(pixelCrop); @@ -96,13 +96,13 @@ void CenterDisplayOutputRect(float *x, float *y, float *w, float *h, float origW outW = frameW; outH = frameW / origRatio; // Stretch a little bit - if (!rotated && g_Config.iSmallDisplayZoomType == 1) // Partial Stretch + if (!rotated && g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::PARTIAL_STRETCH) outH = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 } else { // Image is taller than frame. Center horizontally. outW = frameH * origRatio; outH = frameH; - if (rotated && g_Config.iSmallDisplayZoomType == 1) // Partial Stretch + if (rotated && g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::PARTIAL_STRETCH) outW = (frameH + outH) / 2.0f; // (408 + 720) / 2 = 564 } } diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 2e57ee6b72..21aee1ec11 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -7,6 +7,7 @@ #include "math/math_util.h" #include "math/lin/vec3.h" #include "GPU/GPUState.h" +#include "GPU/Common/FramebufferCommon.h" #include "GPU/Common/GPUStateUtils.h" #include "GPU/Math3D.h" #include "Core/Reporting.h" @@ -82,7 +83,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView ConvertProjMatrixToVulkan(flippedMatrix); } - if (g_Config.iRenderingMode == 0 && g_display_rotation != DisplayRotation::ROTATE_0) { + if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE && g_display_rotation != DisplayRotation::ROTATE_0) { flippedMatrix = flippedMatrix * g_display_rot_matrix; } @@ -96,7 +97,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView } else { proj_through.setOrthoVulkan(0.0f, gstate_c.curRTWidth, 0, gstate_c.curRTHeight, 0, 1); } - if (g_Config.iRenderingMode == 0 && g_display_rotation != DisplayRotation::ROTATE_0) { + if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE && g_display_rotation != DisplayRotation::ROTATE_0) { proj_through = proj_through * g_display_rot_matrix; } CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr()); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 6b4864c2c6..4e409ccd60 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -459,7 +459,7 @@ void GPU_GLES::CopyDisplayToOutput() { // If buffered, discard the depth buffer of the backbuffer. Don't even know if we need one. #if 0 #ifdef USING_GLES2 - if (gl_extensions.EXT_discard_framebuffer && g_Config.iRenderingMode != 0) { + if (gl_extensions.EXT_discard_framebuffer && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) { GLenum attachments[] = {GL_DEPTH_EXT, GL_STENCIL_EXT}; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments); } diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 77d45c5191..f6b6e398e7 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -28,6 +28,7 @@ #include "Core/Reporting.h" #include "GPU/GPUState.h" +#include "GPU/Common/TextureCacheCommon.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/Software/SoftGpu.h" #include "GPU/Software/Rasterizer.h" @@ -1081,9 +1082,9 @@ static inline void CalculateSamplingParams(const float ds, const float dt, const levelFrac = 0; } - if (g_Config.iTexFiltering == 3) { + if (g_Config.iTexFiltering == TEX_FILTER_LINEAR) { filt = true; - } else if (g_Config.iTexFiltering == 2) { + } else if (g_Config.iTexFiltering == TEX_FILTER_NEAREST) { filt = false; } else { filt = detail > 0 ? gstate.isMinifyFilteringEnabled() : gstate.isMagnifyFilteringEnabled(); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index a85d32e52e..405c10fc54 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -784,7 +784,7 @@ void DrawEngineVulkan::DoFlush() { if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + Draw::NativeObject object = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, true); if (!pipeline || !pipeline->pipeline) { @@ -860,9 +860,9 @@ void DrawEngineVulkan::DoFlush() { params.transformedExpanded = transformedExpanded; params.fbman = framebufferManager_; params.texCache = textureCache_; - // We have to force drawing of primitives if g_Config.iRenderingMode == 0 (non-buffered) because Vulkan clears + // We have to force drawing of primitives if g_Config.iRenderingMode == FB_NON_BUFFERED_MODE because Vulkan clears // do not respect scissor rects. - params.allowClear = g_Config.iRenderingMode != 0; + params.allowClear = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; params.allowSeparateAlphaClear = false; params.provokeFlatFirst = true; @@ -889,7 +889,7 @@ void DrawEngineVulkan::DoFlush() { if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; + Draw::NativeObject object = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false); if (!pipeline || !pipeline->pipeline) { diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index a41b2b53cf..0ba1901811 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -73,7 +73,7 @@ bool DisplayLayoutScreen::touch(const TouchInput &touch) { using namespace UI; int mode = mode_ ? mode_->GetSelection() : 0; - if (g_Config.iSmallDisplayZoomType == 2) { mode = -1; } + if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::AUTO) { mode = -1; } const Bounds &screen_bounds = screenManager()->getUIContext()->GetBounds(); if ((touch.flags & TOUCH_MOVE) && picked_ != 0) { @@ -186,7 +186,7 @@ UI::EventReturn DisplayLayoutScreen::OnCenter(UI::EventParams &e) { }; UI::EventReturn DisplayLayoutScreen::OnZoomTypeChange(UI::EventParams &e) { - if (g_Config.iSmallDisplayZoomType < 3) { + if (g_Config.iSmallDisplayZoomType < (int)SmallDisplayZoom::MANUAL) { const Bounds &bounds = screenManager()->getUIContext()->GetBounds(); float autoBound = bounds.w / 480.0f; g_Config.fSmallDisplayZoomLevel = autoBound; @@ -269,8 +269,8 @@ void DisplayLayoutScreen::CreateViews() { HighlightLabel *label = nullptr; mode_ = nullptr; - if (g_Config.iSmallDisplayZoomType > 1) { // Scaling - if (g_Config.iSmallDisplayZoomType == 2) { // Auto Scaling + if (g_Config.iSmallDisplayZoomType >= (int)SmallDisplayZoom::AUTO) { // Scaling + if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::AUTO) { label = new HighlightLabel(gr->T("Auto Scaling"), new AnchorLayoutParams(WRAP_CONTENT, 64.0f, local_dp_xres / 2.0f, local_dp_yres / 2.0f, NONE, NONE, true)); float autoBound = local_dp_yres / 270.0f; // Case of screen rotated ~ only works with buffered rendering @@ -312,7 +312,7 @@ void DisplayLayoutScreen::CreateViews() { displayRepresentation_->SetVisibility(V_INVISIBLE); float width = previewWidth; float height = previewHeight; - if (g_Config.iSmallDisplayZoomType == 0) { // Stretched + if (g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::STRETCH) { Choice *stretched = new Choice("", "", false, new AnchorLayoutParams(width, height, width - width / 2.0f, NONE, NONE, height - height / 2.0f)); stretched->SetEnabled(false); root_->Add(stretched); @@ -321,12 +321,12 @@ void DisplayLayoutScreen::CreateViews() { float frameRatio = width / height; if (origRatio > frameRatio) { height = width / origRatio; - if (!bRotated && g_Config.iSmallDisplayZoomType == 1) { + if (!bRotated && g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::PARTIAL_STRETCH) { height = (272.0f + height) / 2.0f; } } else { width = height * origRatio; - if (bRotated && g_Config.iSmallDisplayZoomType == 1) { + if (bRotated && g_Config.iSmallDisplayZoomType == (int)SmallDisplayZoom::PARTIAL_STRETCH) { width = (272.0f + height) / 2.0f; } } diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 1bf76c3a36..cc00b5326d 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -1060,7 +1060,7 @@ namespace MainWindow { CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache); CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.iShowFPSCounter); CHECKITEM(ID_OPTIONS_FRAMESKIP_AUTO, g_Config.bAutoFrameSkip); - CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != 0); + CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != FRAMESKIP_OFF); CHECKITEM(ID_OPTIONS_VSYNC, g_Config.bVSync); CHECKITEM(ID_OPTIONS_TOPMOST, g_Config.bTopMost); CHECKITEM(ID_OPTIONS_PAUSE_FOCUS, g_Config.bPauseOnLostFocus); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 6f939a909a..a03ab6664f 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -18,6 +18,7 @@ #include "Core/HLE/sceUtility.h" #include "Core/Host.h" #include "Core/SaveState.h" +#include "GPU/Common/FramebufferCommon.h" #include "Log.h" #include "LogManager.h" #include "base/NativeApp.h" @@ -349,7 +350,7 @@ int main(int argc, const char* argv[]) // Never report from tests. g_Config.sReportHost = ""; g_Config.bAutoSaveSymbolMap = false; - g_Config.iRenderingMode = 1; + g_Config.iRenderingMode = FB_BUFFERED_MODE; g_Config.bHardwareTransform = true; g_Config.iAnisotropyLevel = 0; // When testing mipmapping we really don't want this. g_Config.bVertexCache = true; From 23ecd2ad62204d42a4a90f593580b9e4a0a005fb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 20:06:00 -0700 Subject: [PATCH 263/301] UI: Make sliders able to show disabled. For alternate speed, we'll allow separate speeds to be "on" or "off". --- ext/native/ui/ui_screen.cpp | 20 +++++++++++++++++--- ext/native/ui/ui_screen.h | 15 +++++++++++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index b4a4edb76b..3339c8d25b 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -516,6 +516,8 @@ EventReturn PopupSliderChoice::HandleClick(EventParams &e) { restoreFocus_ = HasFocus(); SliderPopupScreen *popupScreen = new SliderPopupScreen(value_, minValue_, maxValue_, ChopTitle(text_), step_, units_); + if (!negativeLabel_.empty()) + popupScreen->SetNegativeDisable(negativeLabel_); popupScreen->OnChange.Handle(this, &PopupSliderChoice::HandleChange); if (e.v) popupScreen->SetPopupOrigin(e.v); @@ -541,9 +543,12 @@ void PopupSliderChoice::Draw(UIContext &dc) { int paddingX = 12; dc.SetFontStyle(dc.theme->uiFont); - char temp[32]; + // Always good to have space for Unicode. + char temp[256]; if (zeroLabel_.size() && *value_ == 0) { strcpy(temp, zeroLabel_.c_str()); + } else if (negativeLabel_.size() && *value_ < 0) { + strcpy(temp, negativeLabel_.c_str()); } else { sprintf(temp, fmt_, *value_); } @@ -585,7 +590,7 @@ void PopupSliderChoiceFloat::Draw(UIContext &dc) { int paddingX = 12; dc.SetFontStyle(dc.theme->uiFont); - char temp[32]; + char temp[256]; if (zeroLabel_.size() && *value_ == 0.0f) { strcpy(temp, zeroLabel_.c_str()); } else { @@ -611,6 +616,7 @@ EventReturn SliderPopupScreen::OnDecrease(EventParams ¶ms) { sprintf(temp, "%d", sliderValue_); edit_->SetText(temp); changing_ = false; + disabled_ = false; return EVENT_DONE; } @@ -625,6 +631,7 @@ EventReturn SliderPopupScreen::OnIncrease(EventParams ¶ms) { sprintf(temp, "%d", sliderValue_); edit_->SetText(temp); changing_ = false; + disabled_ = false; return EVENT_DONE; } @@ -634,12 +641,14 @@ EventReturn SliderPopupScreen::OnSliderChange(EventParams ¶ms) { sprintf(temp, "%d", sliderValue_); edit_->SetText(temp); changing_ = false; + disabled_ = false; return EVENT_DONE; } EventReturn SliderPopupScreen::OnTextChange(EventParams ¶ms) { if (!changing_) { sliderValue_ = atoi(edit_->GetText().c_str()); + disabled_ = false; slider_->Clamp(); } return EVENT_DONE; @@ -650,6 +659,8 @@ void SliderPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { UIContext &dc = *screenManager()->getUIContext(); sliderValue_ = *value_; + if (disabled_ && sliderValue_ < 0) + sliderValue_ = 0; LinearLayout *vert = parent->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(UI::Margins(10, 10)))); slider_ = new Slider(&sliderValue_, minValue_, maxValue_, new LinearLayoutParams(UI::Margins(10, 10))); slider_->OnChange.Handle(this, &SliderPopupScreen::OnSliderChange); @@ -672,6 +683,9 @@ void SliderPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { if (!units_.empty()) lin->Add(new TextView(units_, new LinearLayoutParams(10.0f)))->SetTextColor(dc.theme->popupStyle.fgColor); + if (!negativeLabel_.empty()) + vert->Add(new CheckBox(&disabled_, negativeLabel_)); + if (IsFocusMovementEnabled()) UI::SetFocusedView(slider_); } @@ -754,7 +768,7 @@ EventReturn SliderFloatPopupScreen::OnTextChange(EventParams ¶ms) { void SliderPopupScreen::OnCompleted(DialogResult result) { if (result == DR_OK) { - *value_ = sliderValue_; + *value_ = disabled_ ? -1 : sliderValue_; EventParams e{}; e.v = nullptr; e.a = *value_; diff --git a/ext/native/ui/ui_screen.h b/ext/native/ui/ui_screen.h index fb0bda4494..99a6c486eb 100644 --- a/ext/native/ui/ui_screen.h +++ b/ext/native/ui/ui_screen.h @@ -169,9 +169,14 @@ namespace UI { class SliderPopupScreen : public PopupScreen { public: SliderPopupScreen(int *value, int minValue, int maxValue, const std::string &title, int step = 1, const std::string &units = "") - : PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), minValue_(minValue), maxValue_(maxValue), step_(step), changing_(false) {} + : PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), minValue_(minValue), maxValue_(maxValue), step_(step) {} virtual void CreatePopupContents(ViewGroup *parent) override; + void SetNegativeDisable(const std::string &str) { + negativeLabel_ = str; + disabled_ = *value_ < 0; + } + Event OnChange; private: @@ -183,12 +188,14 @@ private: Slider *slider_; UI::TextEdit *edit_; std::string units_; + std::string negativeLabel_; int *value_; int sliderValue_; int minValue_; int maxValue_; int step_; - bool changing_; + bool changing_ = false; + bool disabled_ = false; }; class SliderFloatPopupScreen : public PopupScreen { @@ -325,6 +332,9 @@ public: void SetZeroLabel(const std::string &str) { zeroLabel_ = str; } + void SetNegativeDisable(const std::string &str) { + negativeLabel_ = str; + } Event OnChange; @@ -338,6 +348,7 @@ private: int step_; const char *fmt_; std::string zeroLabel_; + std::string negativeLabel_; std::string units_; ScreenManager *screenManager_; bool restoreFocus_; From 95f270778e35d8200a046228a0bb97b57019b41d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 20:07:11 -0700 Subject: [PATCH 264/301] Core: Allow toggle between 2 custom speeds. In addition to virtual keys for each speed separately. --- Common/KeyMap.cpp | 3 ++- Common/KeyMap.h | 3 ++- Core/Config.cpp | 3 ++- Core/Config.h | 3 ++- Core/CoreParameter.h | 26 ++++++++++++++---------- Core/HLE/sceDisplay.cpp | 35 +++++++++++++++++++------------- GPU/Directx9/GPU_DX9.cpp | 2 +- GPU/GLES/GPU_GLES.cpp | 5 +++-- UI/EmuScreen.cpp | 41 ++++++++++++++++++++++++-------------- UI/GameSettingsScreen.cpp | 18 ++++++++++++----- UI/GameSettingsScreen.h | 3 ++- Windows/MainWindowMenu.cpp | 4 ---- 12 files changed, 90 insertions(+), 56 deletions(-) diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index 805bd4b4d2..092ca95144 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -645,7 +645,8 @@ const KeyMap_IntStrPair psp_button_names[] = { {VIRTKEY_RAPID_FIRE, "RapidFire"}, {VIRTKEY_UNTHROTTLE, "Unthrottle"}, {VIRTKEY_SPEED_TOGGLE, "SpeedToggle"}, - {VIRTKEY_SPEED_PRESS, "Speed toggle hold"}, + {VIRTKEY_SPEED_CUSTOM1, "Alt speed 1"}, + {VIRTKEY_SPEED_CUSTOM2, "Alt speed 2"}, {VIRTKEY_PAUSE, "Pause"}, #ifndef MOBILE_DEVICE {VIRTKEY_FRAME_ADVANCE, "Frame Advance"}, diff --git a/Common/KeyMap.h b/Common/KeyMap.h index 810beec448..9f408ed7f6 100644 --- a/Common/KeyMap.h +++ b/Common/KeyMap.h @@ -52,7 +52,8 @@ enum { VIRTKEY_DEVMENU = 0x40000014, VIRTKEY_FRAME_ADVANCE = 0x40000015, VIRTKEY_RECORD = 0x40000016, - VIRTKEY_SPEED_PRESS = 0x40000017, + VIRTKEY_SPEED_CUSTOM1 = 0x40000017, + VIRTKEY_SPEED_CUSTOM2 = 0x40000018, VIRTKEY_LAST, VIRTKEY_COUNT = VIRTKEY_LAST - VIRTKEY_FIRST }; diff --git a/Core/Config.cpp b/Core/Config.cpp index cb4044d8d1..810b9102b7 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -525,7 +525,8 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("HighQualityDepth", &g_Config.bHighQualityDepth, true, true, true), ReportedConfigSetting("FrameSkip", &g_Config.iFrameSkip, 0, true, true), ReportedConfigSetting("AutoFrameSkip", &g_Config.bAutoFrameSkip, false, true, true), - ConfigSetting("FrameRate", &g_Config.iFpsLimit, 0, true, true), + ConfigSetting("FrameRate", &g_Config.iFpsLimit1, 0, true, true), + ConfigSetting("FrameRate2", &g_Config.iFpsLimit2, -1, true, true), ConfigSetting("FrameSkipUnthrottle", &g_Config.bFrameSkipUnthrottle, &DefaultFrameskipUnthrottle, true, false), #if defined(USING_WIN_UI) ConfigSetting("RestartRequired", &g_Config.bRestartRequired, false, false), diff --git a/Core/Config.h b/Core/Config.h index 3898eba28e..da179fa8d3 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -160,7 +160,8 @@ public: int iTexScalingLevel; // 0 = auto, 1 = off, 2 = 2x, ..., 5 = 5x int iTexScalingType; // 0 = xBRZ, 1 = Hybrid bool bTexDeposterize; - int iFpsLimit; + int iFpsLimit1; + int iFpsLimit2; int iForceMaxEmulatedFPS; int iMaxRecent; int iCurrentStateSlot; diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index 9e6ec3ef18..ce14107ba1 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -31,6 +31,12 @@ enum GPUCore { GPUCORE_VULKAN, }; +enum class FPSLimit { + NORMAL = 0, + CUSTOM1 = 1, + CUSTOM2 = 2, +}; + class FileLoader; class GraphicsContext; @@ -42,13 +48,13 @@ enum class CPUCore; // PSP_CoreParameter() struct CoreParameter { - CoreParameter() : thin3d(nullptr), collectEmuLog(0), unthrottle(false), fpsLimit(0), updateRecent(true), freezeNext(false), frozen(false), mountIsoLoader(nullptr) {} + CoreParameter() {} CPUCore cpuCore; GPUCore gpuCore; - GraphicsContext *graphicsContext; // TODO: Find a better place. - Draw::DrawContext *thin3d; + GraphicsContext *graphicsContext = nullptr; // TODO: Find a better place. + Draw::DrawContext *thin3d = nullptr; bool enableSound; // there aren't multiple sound cores. std::string fileToStart; @@ -58,7 +64,7 @@ struct CoreParameter { bool startPaused; bool printfEmuLog; // writes "emulator:" logging to stdout - std::string *collectEmuLog; + std::string *collectEmuLog = nullptr; bool headLess; // Try to avoid messageboxes etc // Internal PSP resolution @@ -70,16 +76,16 @@ struct CoreParameter { int pixelHeight; // Can be modified at runtime. - bool unthrottle; - int fpsLimit; + bool unthrottle = false; + FPSLimit fpsLimit = FPSLimit::NORMAL; - bool updateRecent; + bool updateRecent = true; // Freeze-frame. For nvidia perfhud profiling. Developers only. - bool freezeNext; - bool frozen; + bool freezeNext = false; + bool frozen = false; - FileLoader *mountIsoLoader; + FileLoader *mountIsoLoader = nullptr; Compatibility compat; }; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 42c8bd6310..b86d467df8 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -476,17 +476,17 @@ void __DisplayGetDebugStats(char *stats, size_t bufsize) { statbuf); } -enum { - FPS_LIMIT_NORMAL = 0, - FPS_LIMIT_CUSTOM = 1, -}; + void __DisplaySetWasPaused() { wasPaused = true; } static bool FrameTimingThrottled() { - if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit == 0) { + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit1 == 0) { + return false; + } + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 == 0) { return false; } return !PSP_CoreParameter().unthrottle; @@ -505,7 +505,7 @@ static void DoFrameDropLogging(float scaledTimestep) { // Let's collect all the throttling and frameskipping logic here. static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { PROFILE_THIS_SCOPE("timing"); - int fpsLimiter = PSP_CoreParameter().fpsLimit; + FPSLimit fpsLimiter = PSP_CoreParameter().fpsLimit; throttle = FrameTimingThrottled(); skipFrame = false; @@ -528,8 +528,10 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { time_update(); float scaledTimestep = timestep; - if (fpsLimiter == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit != 0) { - scaledTimestep *= 60.0f / g_Config.iFpsLimit; + if (fpsLimiter == FPSLimit::CUSTOM1 && g_Config.iFpsLimit1 > 0) { + scaledTimestep *= 60.0f / g_Config.iFpsLimit1; + } else if (fpsLimiter == FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 > 0) { + scaledTimestep *= 60.0f / g_Config.iFpsLimit2; } if (lastFrameTime == 0.0 || wasPaused) { @@ -549,7 +551,8 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { // Auto-frameskip automatically if speed limit is set differently than the default. bool useAutoFrameskip = g_Config.bAutoFrameSkip && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; - if (g_Config.bAutoFrameSkip || (g_Config.iFrameSkip == 0 && fpsLimiter == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit > 60)) { + bool forceFrameskip = (fpsLimiter == FPSLimit::CUSTOM1 && g_Config.iFpsLimit1 > 60) || (fpsLimiter == FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 > 60); + if (g_Config.bAutoFrameSkip || forceFrameskip) { // autoframeskip // Argh, we are falling behind! Let's skip a frame and see if we catch up. if (curFrameTime > nextFrameTime && doFrameSkip) { @@ -601,9 +604,11 @@ static void DoFrameIdleTiming() { } float scaledVblank = timePerVblank; - if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM) { + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit1 > 0) { // 0 is handled in FrameTimingThrottled(). - scaledVblank *= 60.0f / g_Config.iFpsLimit; + scaledVblank *= 60.0f / g_Config.iFpsLimit1; + } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 > 0) { + scaledVblank *= 60.0f / g_Config.iFpsLimit2; } // If we have over at least a vblank of spare time, maintain at least 30fps in delay. @@ -695,7 +700,7 @@ void __DisplayFlip(int cyclesLate) { // Let the user know if we're running slow, so they know to adjust settings. // Sometimes users just think the sound emulation is broken. static bool hasNotifiedSlow = false; - if (!g_Config.bHideSlowWarnings && !hasNotifiedSlow && PSP_CoreParameter().fpsLimit == FPS_LIMIT_NORMAL && IsRunningSlow()) { + if (!g_Config.bHideSlowWarnings && !hasNotifiedSlow && PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && IsRunningSlow()) { #ifndef _DEBUG I18NCategory *err = GetI18NCategory("Error"); if (g_Config.bSoftwareRendering) { @@ -787,9 +792,11 @@ void hleLagSync(u64 userdata, int cyclesLate) { } float scale = 1.0f; - if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM) { + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit1 > 0) { // 0 is handled in FrameTimingThrottled(). - scale = 60.0f / g_Config.iFpsLimit; + scale = 60.0f / g_Config.iFpsLimit1; + } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 > 0) { + scale = 60.0f / g_Config.iFpsLimit2; } const double goal = lastLagSync + (scale / 1000.0f); diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 24aa119525..afd5a86d10 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -211,7 +211,7 @@ void GPU_DX9::ReapplyGfxState() { void GPU_DX9::BeginFrame() { // Turn off vsync when unthrottled int desiredVSyncInterval = g_Config.bVSync ? 1 : 0; - if ((PSP_CoreParameter().unthrottle) || (PSP_CoreParameter().fpsLimit == 1)) + if (PSP_CoreParameter().unthrottle || PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) desiredVSyncInterval = 0; if (desiredVSyncInterval != lastVsync_) { dxstate.SetVSyncInterval(desiredVSyncInterval); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 4e409ccd60..5eca0e9296 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -392,9 +392,10 @@ inline void GPU_GLES::UpdateVsyncInterval(bool force) { if (PSP_CoreParameter().unthrottle) { desiredVSyncInterval = 0; } - if (PSP_CoreParameter().fpsLimit == 1) { + if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) { + int limit = PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 ? g_Config.iFpsLimit1 : g_Config.iFpsLimit2; // For an alternative speed that is a clean factor of 60, the user probably still wants vsync. - if (g_Config.iFpsLimit == 0 || (g_Config.iFpsLimit != 15 && g_Config.iFpsLimit != 30 && g_Config.iFpsLimit != 60)) { + if (limit == 0 || (limit >= 0 && limit != 15 && limit != 30 && limit != 60)) { desiredVSyncInterval = 0; } } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 8d4baa4f81..2b68d2dce0 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -472,21 +472,31 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { break; case VIRTKEY_SPEED_TOGGLE: - if (PSP_CoreParameter().fpsLimit == 0) { - PSP_CoreParameter().fpsLimit = 1; + // Cycle through enabled speeds. + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); - } else if (PSP_CoreParameter().fpsLimit == 1) { - PSP_CoreParameter().fpsLimit = 0; + } else if (PSP_CoreParameter().fpsLimit != FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 >= 0) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; + osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + } else if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; osm.Show(sc->T("standard", "Speed: standard"), 1.0); } break; - case VIRTKEY_SPEED_PRESS: - if (PSP_CoreParameter().fpsLimit == 0) { - PSP_CoreParameter().fpsLimit = 1; + case VIRTKEY_SPEED_CUSTOM1: + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); } break; + case VIRTKEY_SPEED_CUSTOM2: + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; + osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + } + break; case VIRTKEY_PAUSE: pauseTrigger_ = true; @@ -594,9 +604,15 @@ void EmuScreen::onVKeyUp(int virtualKeyCode) { PSP_CoreParameter().unthrottle = false; break; - case VIRTKEY_SPEED_PRESS: - if (PSP_CoreParameter().fpsLimit == 1) { - PSP_CoreParameter().fpsLimit = 0; + case VIRTKEY_SPEED_CUSTOM1: + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; + osm.Show(sc->T("standard", "Speed: standard"), 1.0); + } + break; + case VIRTKEY_SPEED_CUSTOM2: + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; osm.Show(sc->T("standard", "Speed: standard"), 1.0); } break; @@ -1022,11 +1038,6 @@ void EmuScreen::update() { // Virtual keys. __CtrlSetRapidFire(virtKeys[VIRTKEY_RAPID_FIRE - VIRTKEY_FIRST]); - // Make sure fpsLimit starts at 0 - if (PSP_CoreParameter().fpsLimit != 0 && PSP_CoreParameter().fpsLimit != 1) { - PSP_CoreParameter().fpsLimit = 0; - } - // This is here to support the iOS on screen back button. if (pauseTrigger_) { pauseTrigger_ = false; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f4e45b3012..6e76db39e7 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -127,7 +127,8 @@ void GameSettingsScreen::CreateViews() { cap60FPS_ = g_Config.iForceMaxEmulatedFPS == 60; - iAlternateSpeedPercent_ = (g_Config.iFpsLimit * 100) / 60; + iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60; + iAlternateSpeedPercent2_ = g_Config.iFpsLimit2 < 0 ? -1 : (g_Config.iFpsLimit2 * 100) / 60; bool vertical = UseVerticalLayout(); @@ -271,9 +272,15 @@ void GameSettingsScreen::CreateViews() { frameSkipAuto_->OnClick.Handle(this, &GameSettingsScreen::OnAutoFrameskip); graphicsSettings->Add(new CheckBox(&cap60FPS_, gr->T("Force max 60 FPS (helps GoW)"))); - PopupSliderChoice *altSpeed = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent_, 0, 1000, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited"))); - altSpeed->SetFormat("%i%%"); - altSpeed->SetZeroLabel(gr->T("Unlimited")); + PopupSliderChoice *altSpeed1 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent1_, 0, 1000, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited"))); + altSpeed1->SetFormat("%i%%"); + altSpeed1->SetZeroLabel(gr->T("Unlimited")); + altSpeed1->SetNegativeDisable(gr->T("Disabled")); + + PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent2_, 0, 1000, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited"))); + altSpeed2->SetFormat("%i%%"); + altSpeed2->SetZeroLabel(gr->T("Unlimited")); + altSpeed2->SetNegativeDisable(gr->T("Disabled")); graphicsSettings->Add(new ItemHeader(gr->T("Features"))); // Hide postprocess option on unsupported backends to avoid confusion. @@ -1033,7 +1040,8 @@ void GameSettingsScreen::update() { UIScreen::update(); g_Config.iForceMaxEmulatedFPS = cap60FPS_ ? 60 : 0; - g_Config.iFpsLimit = (iAlternateSpeedPercent_ * 60) / 100; + g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100; + g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100; bool vertical = UseVerticalLayout(); if (vertical != lastVertical_) { diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index c68fb8939a..259c8cf58b 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -106,7 +106,8 @@ private: // Temporaries to convert bools to int settings bool cap60FPS_; - int iAlternateSpeedPercent_; + int iAlternateSpeedPercent1_; + int iAlternateSpeedPercent2_; bool enableReports_; //edit the game-specific settings and restore the global settings after exiting diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index cc00b5326d..56316c620a 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -515,10 +515,6 @@ namespace MainWindow { NativeMessageReceived("gpu_resized", ""); } - static void setFpsLimit(int fps) { - g_Config.iFpsLimit = fps; - } - static void setFrameSkipping(int framesToSkip = -1) { if (framesToSkip >= FRAMESKIP_OFF) g_Config.iFrameSkip = framesToSkip; From 69dd59d9b9ccf872a1e8152028b2df6fc0138c4a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 22:14:41 -0700 Subject: [PATCH 265/301] UI: Refactor touch control config handling. Much cleaner this way, less repetition. --- Core/Config.cpp | 203 +++++++---------- Core/Config.h | 72 ++---- UI/GamepadEmu.cpp | 326 +++++++++------------------- UI/TouchControlLayoutScreen.cpp | 64 +++--- UI/TouchControlVisibilityScreen.cpp | 24 +- 5 files changed, 252 insertions(+), 437 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 810b9102b7..a0cd76f37f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -64,6 +64,7 @@ struct ConfigSetting { TYPE_UINT32, TYPE_FLOAT, TYPE_STRING, + TYPE_TOUCH_POS, }; union Value { bool b; @@ -71,6 +72,7 @@ struct ConfigSetting { uint32_t u; float f; const char *s; + ConfigTouchPos touchPos; }; union SettingPtr { bool *b; @@ -78,6 +80,7 @@ struct ConfigSetting { uint32_t *u; float *f; std::string *s; + ConfigTouchPos *touchPos; }; typedef bool (*BoolDefaultCallback)(); @@ -85,6 +88,7 @@ struct ConfigSetting { typedef uint32_t (*Uint32DefaultCallback)(); typedef float (*FloatDefaultCallback)(); typedef const char *(*StringDefaultCallback)(); + typedef ConfigTouchPos (*TouchPosDefaultCallback)(); union Callback { BoolDefaultCallback b; @@ -92,6 +96,7 @@ struct ConfigSetting { Uint32DefaultCallback u; FloatDefaultCallback f; StringDefaultCallback s; + TouchPosDefaultCallback touchPos; }; ConfigSetting(bool v) @@ -135,6 +140,13 @@ struct ConfigSetting { default_.s = def; } + ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigTouchPos *v, ConfigTouchPos def, bool save = true, bool perGame = false) + : ini_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(TYPE_TOUCH_POS), report_(false), save_(save), perGame_(perGame) { + ptr_.touchPos = v; + cb_.touchPos = nullptr; + default_.touchPos = def; + } + ConfigSetting(const char *ini, bool *v, BoolDefaultCallback def, bool save = true, bool perGame = false) : ini_(ini), type_(TYPE_BOOL), report_(false), save_(save), perGame_(perGame) { ptr_.b = v; @@ -165,6 +177,12 @@ struct ConfigSetting { cb_.s = def; } + ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigTouchPos *v, TouchPosDefaultCallback def, bool save = true, bool perGame = false) + : ini_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(TYPE_TOUCH_POS), report_(false), save_(save), perGame_(perGame) { + ptr_.touchPos = v; + cb_.touchPos = def; + } + bool HasMore() const { return type_ != TYPE_TERMINATOR; } @@ -196,6 +214,19 @@ struct ConfigSetting { default_.s = cb_.s(); } return section->Get(ini_, ptr_.s, default_.s); + case TYPE_TOUCH_POS: + if (cb_.touchPos) { + default_.touchPos = cb_.touchPos(); + } + section->Get(ini_, &ptr_.touchPos->x, default_.touchPos.x); + section->Get(ini2_, &ptr_.touchPos->y, default_.touchPos.y); + section->Get(ini3_, &ptr_.touchPos->scale, default_.touchPos.scale); + if (ini4_) { + section->Get(ini4_, &ptr_.touchPos->show, default_.touchPos.show); + } else { + ptr_.touchPos->show = default_.touchPos.show; + } + return true; default: _dbg_assert_msg_(LOADER, false, "Unexpected ini setting type"); return false; @@ -217,6 +248,14 @@ struct ConfigSetting { return section->Set(ini_, *ptr_.f); case TYPE_STRING: return section->Set(ini_, *ptr_.s); + case TYPE_TOUCH_POS: + section->Set(ini_, ptr_.touchPos->x); + section->Set(ini2_, ptr_.touchPos->y); + section->Set(ini3_, ptr_.touchPos->scale); + if (ini4_) { + section->Set(ini4_, ptr_.touchPos->show); + } + return; default: _dbg_assert_msg_(LOADER, false, "Unexpected ini setting type"); return; @@ -238,6 +277,9 @@ struct ConfigSetting { return data.Add(prefix + ini_, *ptr_.f); case TYPE_STRING: return data.Add(prefix + ini_, *ptr_.s); + case TYPE_TOUCH_POS: + // Doesn't report. + return; default: _dbg_assert_msg_(LOADER, false, "Unexpected ini setting type"); return; @@ -245,6 +287,9 @@ struct ConfigSetting { } const char *ini_; + const char *ini2_; + const char *ini3_; + const char *ini4_; Type type_; bool report_; bool save_; @@ -612,6 +657,8 @@ static bool DefaultShowTouchControls() { } static const float defaultControlScale = 1.15f; +static const ConfigTouchPos defaultTouchPosShow = { -1.0f, -1.0f, defaultControlScale, true }; +static const ConfigTouchPos defaultTouchPosHide = { -1.0f, -1.0f, defaultControlScale, false }; static ConfigSetting controlSettings[] = { ConfigSetting("HapticFeedback", &g_Config.bHapticFeedback, false, true, true), @@ -619,19 +666,7 @@ static ConfigSetting controlSettings[] = { ConfigSetting("ShowTouchCircle", &g_Config.bShowTouchCircle, true, true, true), ConfigSetting("ShowTouchSquare", &g_Config.bShowTouchSquare, true, true, true), ConfigSetting("ShowTouchTriangle", &g_Config.bShowTouchTriangle, true, true, true), - ConfigSetting("ShowTouchStart", &g_Config.bShowTouchStart, true, true, true), - ConfigSetting("ShowTouchSelect", &g_Config.bShowTouchSelect, true, true, true), - ConfigSetting("ShowTouchLTrigger", &g_Config.bShowTouchLTrigger, true, true, true), - ConfigSetting("ShowTouchRTrigger", &g_Config.bShowTouchRTrigger, true, true, true), - ConfigSetting("ShowAnalogStick", &g_Config.bShowTouchAnalogStick, true, true, true), - ConfigSetting("ShowTouchDpad", &g_Config.bShowTouchDpad, true, true, true), - ConfigSetting("ShowTouchUnthrottle", &g_Config.bShowTouchUnthrottle, true, true, true), - ConfigSetting("ShowComboKey0", &g_Config.bShowComboKey0, false, true, true), - ConfigSetting("ShowComboKey1", &g_Config.bShowComboKey1, false, true, true), - ConfigSetting("ShowComboKey2", &g_Config.bShowComboKey2, false, true, true), - ConfigSetting("ShowComboKey3", &g_Config.bShowComboKey3, false, true, true), - ConfigSetting("ShowComboKey4", &g_Config.bShowComboKey4, false, true, true), ConfigSetting("ComboKey0Mapping", &g_Config.iCombokey0, 0, true, true), ConfigSetting("ComboKey1Mapping", &g_Config.iCombokey1, 0, true, true), ConfigSetting("ComboKey2Mapping", &g_Config.iCombokey2, 0, true, true), @@ -671,49 +706,24 @@ static ConfigSetting controlSettings[] = { // -1.0f means uninitialized, set in GamepadEmu::CreatePadLayout(). ConfigSetting("ActionButtonSpacing2", &g_Config.fActionButtonSpacing, 1.0f, true, true), - ConfigSetting("ActionButtonCenterX", &g_Config.fActionButtonCenterX, -1.0f, true, true), - ConfigSetting("ActionButtonCenterY", &g_Config.fActionButtonCenterY, -1.0f, true, true), - ConfigSetting("ActionButtonScale", &g_Config.fActionButtonScale, defaultControlScale, true, true), - ConfigSetting("DPadX", &g_Config.fDpadX, -1.0f, true, true), - ConfigSetting("DPadY", &g_Config.fDpadY, -1.0f, true, true), + ConfigSetting("ActionButtonCenterX", "ActionButtonCenterY", "ActionButtonScale", nullptr, &g_Config.touchActionButtonCenter, defaultTouchPosShow, true, true), + ConfigSetting("DPadX", "DPadY", "DPadScale", "ShowTouchDpad", &g_Config.touchDpad, defaultTouchPosShow, true, true), // Note: these will be overwritten if DPadRadius is set. - ConfigSetting("DPadScale", &g_Config.fDpadScale, defaultControlScale, true, true), ConfigSetting("DPadSpacing", &g_Config.fDpadSpacing, 1.0f, true, true), - ConfigSetting("StartKeyX", &g_Config.fStartKeyX, -1.0f, true, true), - ConfigSetting("StartKeyY", &g_Config.fStartKeyY, -1.0f, true, true), - ConfigSetting("StartKeyScale", &g_Config.fStartKeyScale, defaultControlScale, true, true), - ConfigSetting("SelectKeyX", &g_Config.fSelectKeyX, -1.0f, true, true), - ConfigSetting("SelectKeyY", &g_Config.fSelectKeyY, -1.0f, true, true), - ConfigSetting("SelectKeyScale", &g_Config.fSelectKeyScale, defaultControlScale, true, true), - ConfigSetting("UnthrottleKeyX", &g_Config.fUnthrottleKeyX, -1.0f, true, true), - ConfigSetting("UnthrottleKeyY", &g_Config.fUnthrottleKeyY, -1.0f, true, true), - ConfigSetting("UnthrottleKeyScale", &g_Config.fUnthrottleKeyScale, defaultControlScale, true, true), - ConfigSetting("LKeyX", &g_Config.fLKeyX, -1.0f, true, true), - ConfigSetting("LKeyY", &g_Config.fLKeyY, -1.0f, true, true), - ConfigSetting("LKeyScale", &g_Config.fLKeyScale, defaultControlScale, true, true), - ConfigSetting("RKeyX", &g_Config.fRKeyX, -1.0f, true, true), - ConfigSetting("RKeyY", &g_Config.fRKeyY, -1.0f, true, true), - ConfigSetting("RKeyScale", &g_Config.fRKeyScale, defaultControlScale, true, true), - ConfigSetting("AnalogStickX", &g_Config.fAnalogStickX, -1.0f, true, true), - ConfigSetting("AnalogStickY", &g_Config.fAnalogStickY, -1.0f, true, true), - ConfigSetting("AnalogStickScale", &g_Config.fAnalogStickScale, defaultControlScale, true, true), + ConfigSetting("StartKeyX", "StartKeyY", "StartKeyScale", "ShowTouchStart", &g_Config.touchStartKey, defaultTouchPosShow, true, true), + ConfigSetting("SelectKeyX", "SelectKeyY", "SelectKeyScale", "ShowTouchSelect", &g_Config.touchSelectKey, defaultTouchPosShow, true, true), + ConfigSetting("UnthrottleKeyX", "UnthrottleKeyY", "UnthrottleKeyScale", "ShowTouchUnthrottle", &g_Config.touchUnthrottleKey, defaultTouchPosShow, true, true), + ConfigSetting("LKeyX", "LKeyY", "LKeyScale", "ShowTouchLTrigger", &g_Config.touchLKey, defaultTouchPosShow, true, true), + ConfigSetting("RKeyX", "RKeyY", "RKeyScale", "ShowTouchRTrigger", &g_Config.touchRKey, defaultTouchPosShow, true, true), + ConfigSetting("AnalogStickX", "AnalogStickY", "AnalogStickScale", "ShowAnalogStick", &g_Config.touchAnalogStick, defaultTouchPosShow, true, true), + + ConfigSetting("fcombo0X", "fcombo0Y", "comboKeyScale0", "ShowComboKey0", &g_Config.touchCombo0, defaultTouchPosHide, true, true), + ConfigSetting("fcombo1X", "fcombo1Y", "comboKeyScale1", "ShowComboKey1", &g_Config.touchCombo1, defaultTouchPosHide, true, true), + ConfigSetting("fcombo2X", "fcombo2Y", "comboKeyScale2", "ShowComboKey2", &g_Config.touchCombo2, defaultTouchPosHide, true, true), + ConfigSetting("fcombo3X", "fcombo3Y", "comboKeyScale3", "ShowComboKey3", &g_Config.touchCombo3, defaultTouchPosHide, true, true), + ConfigSetting("fcombo4X", "fcombo4Y", "comboKeyScale4", "ShowComboKey4", &g_Config.touchCombo4, defaultTouchPosHide, true, true), - ConfigSetting("fcombo0X", &g_Config.fcombo0X, -1.0f, true, true), - ConfigSetting("fcombo0Y", &g_Config.fcombo0Y, -1.0f, true, true), - ConfigSetting("comboKeyScale0", &g_Config.fcomboScale0, defaultControlScale, true, true), - ConfigSetting("fcombo1X", &g_Config.fcombo1X, -1.0f, true, true), - ConfigSetting("fcombo1Y", &g_Config.fcombo1Y, -1.0f, true, true), - ConfigSetting("comboKeyScale1", &g_Config.fcomboScale1, defaultControlScale, true, true), - ConfigSetting("fcombo2X", &g_Config.fcombo2X, -1.0f, true, true), - ConfigSetting("fcombo2Y", &g_Config.fcombo2Y, -1.0f, true, true), - ConfigSetting("comboKeyScale2", &g_Config.fcomboScale2, defaultControlScale, true, true), - ConfigSetting("fcombo3X", &g_Config.fcombo3X, -1.0f, true, true), - ConfigSetting("fcombo3Y", &g_Config.fcombo3Y, -1.0f, true, true), - ConfigSetting("comboKeyScale3", &g_Config.fcomboScale3, defaultControlScale, true, true), - ConfigSetting("fcombo4X", &g_Config.fcombo4X, -1.0f, true, true), - ConfigSetting("fcombo4Y", &g_Config.fcombo4Y, -1.0f, true, true), - ConfigSetting("comboKeyScale4", &g_Config.fcomboScale4, defaultControlScale, true, true), #ifdef _WIN32 ConfigSetting("DInputAnalogDeadzone", &g_Config.fDInputAnalogDeadzone, 0.1f, true, true), ConfigSetting("DInputAnalogInverseMode", &g_Config.iDInputAnalogInverseMode, 0, true, true), @@ -1001,40 +1011,6 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { ResetControlLayout(); } - // MIGRATION: For users who had the old static touch layout, aren't I nice? - // We can probably kill this in 0.9.8 or something. - if (fDpadX > 1.0 || fDpadY > 1.0) { // Likely the rest are too! - float screen_width = dp_xres; - float screen_height = dp_yres; - - fActionButtonCenterX /= screen_width; - fActionButtonCenterY /= screen_height; - fDpadX /= screen_width; - fDpadY /= screen_height; - fStartKeyX /= screen_width; - fStartKeyY /= screen_height; - fSelectKeyX /= screen_width; - fSelectKeyY /= screen_height; - fUnthrottleKeyX /= screen_width; - fUnthrottleKeyY /= screen_height; - fLKeyX /= screen_width; - fLKeyY /= screen_height; - fRKeyX /= screen_width; - fRKeyY /= screen_height; - fAnalogStickX /= screen_width; - fAnalogStickY /= screen_height; - fcombo0X /= screen_width; - fcombo0Y /= screen_height; - fcombo1X /= screen_width; - fcombo1Y /= screen_height; - fcombo2X /= screen_width; - fcombo2Y /= screen_height; - fcombo3X /= screen_width; - fcombo3Y /= screen_height; - fcombo4X /= screen_width; - fcombo4Y /= screen_height; - } - const char *gitVer = PPSSPP_GIT_VERSION; Version installed(gitVer); Version upgrade(upgradeVersion); @@ -1432,47 +1408,26 @@ void Config::LoadStandardControllerIni() { } void Config::ResetControlLayout() { - g_Config.fActionButtonScale = defaultControlScale; + auto reset = [](ConfigTouchPos &pos) { + pos.x = defaultTouchPosShow.x; + pos.y = defaultTouchPosShow.y; + pos.scale = defaultTouchPosShow.scale; + }; + reset(g_Config.touchActionButtonCenter); g_Config.fActionButtonSpacing = 1.0f; - g_Config.fActionButtonCenterX = -1.0; - g_Config.fActionButtonCenterY = -1.0; - g_Config.fDpadScale = defaultControlScale; + reset(g_Config.touchDpad); g_Config.fDpadSpacing = 1.0f; - g_Config.fDpadX = -1.0; - g_Config.fDpadY = -1.0; - g_Config.fStartKeyX = -1.0; - g_Config.fStartKeyY = -1.0; - g_Config.fStartKeyScale = defaultControlScale; - g_Config.fSelectKeyX = -1.0; - g_Config.fSelectKeyY = -1.0; - g_Config.fSelectKeyScale = defaultControlScale; - g_Config.fUnthrottleKeyX = -1.0; - g_Config.fUnthrottleKeyY = -1.0; - g_Config.fUnthrottleKeyScale = defaultControlScale; - g_Config.fLKeyX = -1.0; - g_Config.fLKeyY = -1.0; - g_Config.fLKeyScale = defaultControlScale; - g_Config.fRKeyX = -1.0; - g_Config.fRKeyY = -1.0; - g_Config.fRKeyScale = defaultControlScale; - g_Config.fAnalogStickX = -1.0; - g_Config.fAnalogStickY = -1.0; - g_Config.fAnalogStickScale = defaultControlScale; - g_Config.fcombo0X = -1.0; - g_Config.fcombo0Y = -1.0; - g_Config.fcomboScale0 = defaultControlScale; - g_Config.fcombo1X = -1.0f; - g_Config.fcombo1Y = -1.0f; - g_Config.fcomboScale1 = defaultControlScale; - g_Config.fcombo2X = -1.0f; - g_Config.fcombo2Y = -1.0f; - g_Config.fcomboScale2 = defaultControlScale; - g_Config.fcombo3X = -1.0f; - g_Config.fcombo3Y = -1.0f; - g_Config.fcomboScale3 = defaultControlScale; - g_Config.fcombo4X = -1.0f; - g_Config.fcombo4Y = -1.0f; - g_Config.fcomboScale4 = defaultControlScale; + reset(g_Config.touchStartKey); + reset(g_Config.touchSelectKey); + reset(g_Config.touchUnthrottleKey); + reset(g_Config.touchLKey); + reset(g_Config.touchRKey); + reset(g_Config.touchAnalogStick); + reset(g_Config.touchCombo0); + reset(g_Config.touchCombo1); + reset(g_Config.touchCombo2); + reset(g_Config.touchCombo3); + reset(g_Config.touchCombo4); } void Config::GetReportingInfo(UrlEncoder &data) { diff --git a/Core/Config.h b/Core/Config.h index da179fa8d3..60d12010de 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -32,6 +32,14 @@ namespace http { struct UrlEncoder; +struct ConfigTouchPos { + float x; + float y; + float scale; + // Note: Show is not used for all settings. + bool show; +}; + struct Config { public: Config(); @@ -267,46 +275,25 @@ public: //space between PSP buttons //the PSP button's center (triangle, circle, square, cross) - float fActionButtonCenterX, fActionButtonCenterY; - float fActionButtonScale; + ConfigTouchPos touchActionButtonCenter; float fActionButtonSpacing; //radius of the D-pad (PSP cross) // int iDpadRadius; //the D-pad (PSP cross) position - float fDpadX, fDpadY; - float fDpadScale; + ConfigTouchPos touchDpad; float fDpadSpacing; - //the start key position - float fStartKeyX, fStartKeyY; - float fStartKeyScale; - //the select key position; - float fSelectKeyX, fSelectKeyY; - float fSelectKeyScale; + ConfigTouchPos touchStartKey; + ConfigTouchPos touchSelectKey; + ConfigTouchPos touchUnthrottleKey; + ConfigTouchPos touchLKey; + ConfigTouchPos touchRKey; + ConfigTouchPos touchAnalogStick; - float fUnthrottleKeyX, fUnthrottleKeyY; - float fUnthrottleKeyScale; - - float fLKeyX, fLKeyY; - float fLKeyScale; - - float fRKeyX, fRKeyY; - float fRKeyScale; - - //position of the analog stick - float fAnalogStickX, fAnalogStickY; - float fAnalogStickScale; - - //the Combo Button position - float fcombo0X, fcombo0Y; - float fcomboScale0; - float fcombo1X, fcombo1Y; - float fcomboScale1; - float fcombo2X, fcombo2Y; - float fcomboScale2; - float fcombo3X, fcombo3Y; - float fcomboScale3; - float fcombo4X, fcombo4Y; - float fcomboScale4; + ConfigTouchPos touchCombo0; + ConfigTouchPos touchCombo1; + ConfigTouchPos touchCombo2; + ConfigTouchPos touchCombo3; + ConfigTouchPos touchCombo4; // Controls Visibility bool bShowTouchControls; @@ -316,23 +303,6 @@ public: bool bShowTouchTriangle; bool bShowTouchSquare; - bool bShowTouchStart; - bool bShowTouchSelect; - bool bShowTouchUnthrottle; - - bool bShowTouchLTrigger; - bool bShowTouchRTrigger; - - bool bShowTouchAnalogStick; - bool bShowTouchDpad; - - //Combo Button Visibility - bool bShowComboKey0; - bool bShowComboKey1; - bool bShowComboKey2; - bool bShowComboKey3; - bool bShowComboKey4; - // Combo_key mapping. These are bitfields. int iCombokey0; int iCombokey1; diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 42edfa2979..a0de9fa829 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -387,6 +387,15 @@ void PSPStick::ProcessTouch(float x, float y, bool down) { void InitPadLayout(float xres, float yres, float globalScale) { const float scale = globalScale; + const int halfW = xres / 2; + + auto initTouchPos = [=](ConfigTouchPos &touch, float x, float y) { + if (touch.x == -1.0f || touch.y == -1.0f) { + touch.x = x / xres; + touch.y = y / yres; + touch.scale = scale; + } + }; // PSP buttons (triangle, circle, square, cross)--------------------- // space between the PSP buttons (triangle, circle, square and cross) @@ -398,12 +407,7 @@ void InitPadLayout(float xres, float yres, float globalScale) { float Action_button_spacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing; int Action_button_center_X = xres - Action_button_spacing * 2; int Action_button_center_Y = yres - Action_button_spacing * 2; - - if (g_Config.fActionButtonCenterX == -1.0 || g_Config.fActionButtonCenterY == -1.0) { - // Setup defaults - g_Config.fActionButtonCenterX = (float)Action_button_center_X / xres; - g_Config.fActionButtonCenterY = (float)Action_button_center_Y / yres; - } + initTouchPos(g_Config.touchActionButtonCenter, Action_button_center_X, Action_button_center_Y); //D-PAD (up down left right) (aka PSP cross)---------------------------- //radius to the D-pad @@ -411,26 +415,16 @@ void InitPadLayout(float xres, float yres, float globalScale) { int D_pad_X = 2.5 * D_pad_Radius * scale; int D_pad_Y = yres - D_pad_Radius * scale; - if (g_Config.bShowTouchAnalogStick) { + if (g_Config.touchAnalogStick.show) { D_pad_Y -= 200 * scale; } - - if (g_Config.fDpadX == -1.0 || g_Config.fDpadY == -1.0 ) { - //setup defaults - g_Config.fDpadX = (float)D_pad_X / xres; - g_Config.fDpadY = (float)D_pad_Y / yres; - } + initTouchPos(g_Config.touchDpad, D_pad_X, D_pad_Y); //analog stick------------------------------------------------------- //keep the analog stick right below the D pad int analog_stick_X = D_pad_X; int analog_stick_Y = yres - 80 * scale; - - if (g_Config.fAnalogStickX == -1.0 || g_Config.fAnalogStickY == -1.0 ) { - g_Config.fAnalogStickX = (float)analog_stick_X / xres; - g_Config.fAnalogStickY = (float)analog_stick_Y / yres; - g_Config.fAnalogStickScale = scale; - } + initTouchPos(g_Config.touchAnalogStick, analog_stick_X, analog_stick_Y); //select, start, throttle-------------------------------------------- //space between the bottom keys (space between select, start and un-throttle) @@ -439,32 +433,17 @@ void InitPadLayout(float xres, float yres, float globalScale) { bottom_key_spacing *= 0.8f; } - int start_key_X = xres / 2 + (bottom_key_spacing) * scale; + int start_key_X = halfW + bottom_key_spacing * scale; int start_key_Y = yres - 60 * scale; + initTouchPos(g_Config.touchStartKey, start_key_X, start_key_Y); - if (g_Config.fStartKeyX == -1.0 || g_Config.fStartKeyY == -1.0 ) { - g_Config.fStartKeyX = (float)start_key_X / xres; - g_Config.fStartKeyY = (float)start_key_Y / yres; - g_Config.fStartKeyScale = scale; - } - - int select_key_X = xres / 2; + int select_key_X = halfW; int select_key_Y = yres - 60 * scale; + initTouchPos(g_Config.touchSelectKey, select_key_X, select_key_Y); - if (g_Config.fSelectKeyX == -1.0 || g_Config.fSelectKeyY == -1.0 ) { - g_Config.fSelectKeyX = (float)select_key_X / xres; - g_Config.fSelectKeyY = (float)select_key_Y / yres; - g_Config.fSelectKeyScale = scale; - } - - int unthrottle_key_X = xres / 2 - (bottom_key_spacing) * scale; + int unthrottle_key_X = halfW - bottom_key_spacing * scale; int unthrottle_key_Y = yres - 60 * scale; - - if (g_Config.fUnthrottleKeyX == -1.0 || g_Config.fUnthrottleKeyY == -1.0 ) { - g_Config.fUnthrottleKeyX = (float)unthrottle_key_X / xres; - g_Config.fUnthrottleKeyY = (float)unthrottle_key_Y / yres; - g_Config.fUnthrottleKeyScale = scale; - } + initTouchPos(g_Config.touchUnthrottleKey, unthrottle_key_X, unthrottle_key_Y); // L and R------------------------------------------------------------ // Put them above the analog stick / above the buttons to the right. @@ -472,214 +451,125 @@ void InitPadLayout(float xres, float yres, float globalScale) { int l_key_X = 60 * scale; int l_key_Y = yres - 380 * scale; - - if (g_Config.fLKeyX == -1.0 || g_Config.fLKeyY == -1.0 ) { - g_Config.fLKeyX = (float)l_key_X / xres; - g_Config.fLKeyY = (float)l_key_Y / yres; - g_Config.fLKeyScale = scale; - } + initTouchPos(g_Config.touchLKey, l_key_X, l_key_Y); int r_key_X = xres - 60 * scale; int r_key_Y = l_key_Y; - - if (g_Config.fRKeyX == -1.0 || g_Config.fRKeyY == -1.0 ) { - g_Config.fRKeyX = (float)r_key_X / xres; - g_Config.fRKeyY = (float)r_key_Y / yres; - g_Config.fRKeyScale = scale; - } + initTouchPos(g_Config.touchRKey, r_key_X, r_key_Y); //Combo key - int combo_key_X = xres / 2 + (bottom_key_spacing)* scale*1.2f; + int combo_key_X = halfW + bottom_key_spacing * scale * 1.2f; int combo_key_Y = yres / 2; + initTouchPos(g_Config.touchCombo0, combo_key_X, combo_key_Y); - if (g_Config.fcombo0X == -1.0 || g_Config.fcombo0Y == -1.0) { - g_Config.fcombo0X = (float)combo_key_X / xres; - g_Config.fcombo0Y = (float)combo_key_Y / yres; - g_Config.fcomboScale0 = scale; - } - - int combo1_key_X = xres / 2 + (bottom_key_spacing)* scale * 2.2; + int combo1_key_X = halfW + bottom_key_spacing * scale * 2.2f; int combo1_key_Y = yres / 2; + initTouchPos(g_Config.touchCombo1, combo1_key_X, combo1_key_Y); - if (g_Config.fcombo1X == -1.0 || g_Config.fcombo1Y == -1.0) { - g_Config.fcombo1X = (float)combo1_key_X / xres; - g_Config.fcombo1Y = (float)combo1_key_Y / yres; - g_Config.fcomboScale1 = scale; - } - - int combo2_key_X = xres / 2 + (bottom_key_spacing)* scale * 3.2; + int combo2_key_X = halfW + bottom_key_spacing * scale * 3.2f; int combo2_key_Y = yres / 2; + initTouchPos(g_Config.touchCombo2, combo2_key_X, combo2_key_Y); - if (g_Config.fcombo2X == -1.0 || g_Config.fcombo2Y == -1.0) { - g_Config.fcombo2X = (float)combo2_key_X / xres; - g_Config.fcombo2Y = (float)combo2_key_Y / yres; - g_Config.fcomboScale2 = scale; - } - - int combo3_key_X = xres / 2 + (bottom_key_spacing)* scale * 1.2; + int combo3_key_X = halfW + bottom_key_spacing * scale * 1.2f; int combo3_key_Y = yres / 3; + initTouchPos(g_Config.touchCombo3, combo3_key_X, combo3_key_Y); - if (g_Config.fcombo3X == -1.0 || g_Config.fcombo3Y == -1.0) { - g_Config.fcombo3X = (float)combo3_key_X / xres; - g_Config.fcombo3Y = (float)combo3_key_Y / yres; - g_Config.fcomboScale3 = scale; - } - - int combo4_key_X = xres / 2 + (bottom_key_spacing)* scale * 2.2; + int combo4_key_X = halfW + bottom_key_spacing * scale * 2.2f; int combo4_key_Y = yres / 3; - - if (g_Config.fcombo4X == -1.0 || g_Config.fcombo4Y == -1.0) { - g_Config.fcombo4X = (float)combo4_key_X / xres; - g_Config.fcombo4Y = (float)combo4_key_Y / yres; - g_Config.fcomboScale4 = scale; - } - -}; + initTouchPos(g_Config.touchCombo4, combo4_key_X, combo4_key_Y); +} UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { - //standard coord system - using namespace UI; AnchorLayout *root = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + if (!g_Config.bShowTouchControls) { + return root; + } - //PSP buttons (triangle, circle, square, cross)--------------------- - //space between the PSP buttons (traingle, circle, square and cross) - const float Action_button_scale = g_Config.fActionButtonScale; - const float Action_button_spacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing; - //position of the circle button (the PSP circle button). It is the farthest to the left - float Action_button_center_X = g_Config.fActionButtonCenterX * xres; - float Action_button_center_Y = g_Config.fActionButtonCenterY * yres; + struct ButtonOffset { + float x; + float y; + }; + auto buttonLayoutParams = [=](const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) { + return new AnchorLayoutParams(touch.x * xres + off.x, touch.y * yres + off.y, NONE, NONE, true); + }; - const float Action_circle_button_X = Action_button_center_X + Action_button_spacing; - const float Action_circle_button_Y = Action_button_center_Y; - - const float Action_cross_button_X = Action_button_center_X; - const float Action_cross_button_Y = Action_button_center_Y + Action_button_spacing; - - const float Action_triangle_button_X = Action_button_center_X; - const float Action_triangle_button_Y = Action_button_center_Y - Action_button_spacing; - - const float Action_square_button_X = Action_button_center_X - Action_button_spacing; - const float Action_square_button_Y = Action_button_center_Y; - - //D-PAD (up down left right) (aka PSP cross)-------------------------------------------------------------- - //radius to the D-pad - - float D_pad_X = g_Config.fDpadX * xres; - float D_pad_Y = g_Config.fDpadY * yres; - float D_pad_scale = g_Config.fDpadScale; - float D_pad_spacing = g_Config.fDpadSpacing; - - //select, start, throttle-------------------------------------------- - //space between the bottom keys (space between select, start and un-throttle) - float start_key_X = g_Config.fStartKeyX * xres; - float start_key_Y = g_Config.fStartKeyY * yres; - float start_key_scale = g_Config.fStartKeyScale; - - float select_key_X = g_Config.fSelectKeyX * xres; - float select_key_Y = g_Config.fSelectKeyY * yres; - float select_key_scale = g_Config.fSelectKeyScale; - - float unthrottle_key_X = g_Config.fUnthrottleKeyX * xres; - float unthrottle_key_Y = g_Config.fUnthrottleKeyY * yres; - float unthrottle_key_scale = g_Config.fUnthrottleKeyScale; - - //L and R------------------------------------------------------------ - float l_key_X = g_Config.fLKeyX * xres; - float l_key_Y = g_Config.fLKeyY * yres; - float l_key_scale = g_Config.fLKeyScale; - - float r_key_X = g_Config.fRKeyX * xres; - float r_key_Y = g_Config.fRKeyY * yres; - float r_key_scale = g_Config.fRKeyScale; - - //analog stick------------------------------------------------------- - float analog_stick_X = g_Config.fAnalogStickX * xres; - float analog_stick_Y = g_Config.fAnalogStickY * yres; - float analog_stick_scale = g_Config.fAnalogStickScale; - - //combo key ------------------------------------------------------- - float combo0_key_X = g_Config.fcombo0X * xres; - float combo0_key_Y = g_Config.fcombo0Y * yres; - float combo_key_scale = g_Config.fcomboScale0; - float combo1_key_X = g_Config.fcombo1X * xres; - float combo1_key_Y = g_Config.fcombo1Y * yres; - float combo1_key_scale = g_Config.fcomboScale1; - float combo2_key_X = g_Config.fcombo2X * xres; - float combo2_key_Y = g_Config.fcombo2Y * yres; - float combo2_key_scale = g_Config.fcomboScale2; - float combo3_key_X = g_Config.fcombo3X * xres; - float combo3_key_Y = g_Config.fcombo3Y * yres; - float combo3_key_scale = g_Config.fcomboScale3; - float combo4_key_X = g_Config.fcombo4X * xres; - float combo4_key_Y = g_Config.fcombo4Y * yres; - float combo4_key_scale = g_Config.fcomboScale4; + // Space between the PSP buttons (traingle, circle, square and cross) + const float actionButtonSpacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing; + // Position of the circle button (the PSP circle button). It is the farthest to the right. + ButtonOffset circleOffset{ actionButtonSpacing, 0.0f }; + ButtonOffset crossOffset{ 0.0f, actionButtonSpacing }; + ButtonOffset triangleOffset{ 0.0f, -actionButtonSpacing }; + ButtonOffset squareOffset{ -actionButtonSpacing, 0.0f }; const int halfW = xres / 2; const int roundImage = g_Config.iTouchButtonStyle ? I_ROUND_LINE : I_ROUND; - if (g_Config.bShowTouchControls) { - const int rectImage = g_Config.iTouchButtonStyle ? I_RECT_LINE : I_RECT; - const int shoulderImage = g_Config.iTouchButtonStyle ? I_SHOULDER_LINE : I_SHOULDER; - const int dirImage = g_Config.iTouchButtonStyle ? I_DIR_LINE : I_DIR; - const int stickImage = g_Config.iTouchButtonStyle ? I_STICK_LINE : I_STICK; - const int stickBg = g_Config.iTouchButtonStyle ? I_STICK_BG_LINE : I_STICK_BG; - static const int comboKeyImages[5] = { I_1, I_2, I_3, I_4, I_5 }; + const int rectImage = g_Config.iTouchButtonStyle ? I_RECT_LINE : I_RECT; + const int shoulderImage = g_Config.iTouchButtonStyle ? I_SHOULDER_LINE : I_SHOULDER; + const int dirImage = g_Config.iTouchButtonStyle ? I_DIR_LINE : I_DIR; + const int stickImage = g_Config.iTouchButtonStyle ? I_STICK_LINE : I_STICK; + const int stickBg = g_Config.iTouchButtonStyle ? I_STICK_BG_LINE : I_STICK_BG; + static const int comboKeyImages[5] = { I_1, I_2, I_3, I_4, I_5 }; - if (!System_GetPropertyBool(SYSPROP_HAS_BACK_BUTTON) || g_Config.bShowTouchPause) { - root->Add(new BoolButton(pause, roundImage, I_ARROW, 1.0f, new AnchorLayoutParams(halfW, 20, NONE, NONE, true)))->SetAngle(90); + auto addPSPButton = [=](int buttonBit, int bgImg, int img, const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) -> PSPButton * { + if (touch.show) { + return root->Add(new PSPButton(buttonBit, bgImg, img, touch.scale, buttonLayoutParams(touch, off))); } + return nullptr; + }; + auto addComboKey = [=](int buttonBit, int bgImg, int img, const ConfigTouchPos &touch) -> ComboKey * { + if (touch.show) { + return root->Add(new ComboKey(buttonBit, bgImg, img, touch.scale, buttonLayoutParams(touch))); + } + return nullptr; + }; + auto addBoolButton = [=](bool *value, int bgImg, int img, const ConfigTouchPos &touch) -> BoolButton * { + if (touch.show) { + return root->Add(new BoolButton(value, bgImg, img, touch.scale, buttonLayoutParams(touch))); + } + return nullptr; + }; - if (g_Config.bShowTouchCircle) - root->Add(new PSPButton(CTRL_CIRCLE, roundImage, I_CIRCLE, Action_button_scale, new AnchorLayoutParams(Action_circle_button_X, Action_circle_button_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchCross) - root->Add(new PSPButton(CTRL_CROSS, roundImage, I_CROSS, Action_button_scale, new AnchorLayoutParams(Action_cross_button_X, Action_cross_button_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchTriangle) - root->Add(new PSPButton(CTRL_TRIANGLE, roundImage, I_TRIANGLE, Action_button_scale, new AnchorLayoutParams(Action_triangle_button_X, Action_triangle_button_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchSquare) - root->Add(new PSPButton(CTRL_SQUARE, roundImage, I_SQUARE, Action_button_scale, new AnchorLayoutParams(Action_square_button_X, Action_square_button_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchStart) - root->Add(new PSPButton(CTRL_START, rectImage, I_START, start_key_scale, new AnchorLayoutParams(start_key_X, start_key_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchSelect) - root->Add(new PSPButton(CTRL_SELECT, rectImage, I_SELECT, select_key_scale, new AnchorLayoutParams(select_key_X, select_key_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchUnthrottle) - root->Add(new BoolButton(&PSP_CoreParameter().unthrottle, rectImage, I_ARROW, unthrottle_key_scale, new AnchorLayoutParams(unthrottle_key_X, unthrottle_key_Y, NONE, NONE, true)))->SetAngle(180); - - if (g_Config.bShowTouchLTrigger) - root->Add(new PSPButton(CTRL_LTRIGGER, shoulderImage, I_L, l_key_scale, new AnchorLayoutParams(l_key_X, l_key_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchRTrigger) - root->Add(new PSPButton(CTRL_RTRIGGER, shoulderImage, I_R, r_key_scale, new AnchorLayoutParams(r_key_X,r_key_Y, NONE, NONE, true)))->FlipImageH(true); - - if (g_Config.bShowTouchDpad) - root->Add(new PSPDpad(dirImage, I_ARROW, D_pad_scale, D_pad_spacing, new AnchorLayoutParams(D_pad_X, D_pad_Y, NONE, NONE, true))); - - if (g_Config.bShowTouchAnalogStick) - root->Add(new PSPStick(stickBg, stickImage, 0, analog_stick_scale, new AnchorLayoutParams(analog_stick_X, analog_stick_Y, NONE, NONE, true))); - - if (g_Config.bShowComboKey0) - root->Add(new ComboKey(g_Config.iCombokey0, roundImage, comboKeyImages[0], combo_key_scale, new AnchorLayoutParams(combo0_key_X, combo0_key_Y, NONE, NONE, true))); - - if (g_Config.bShowComboKey1) - root->Add(new ComboKey(g_Config.iCombokey1, roundImage, comboKeyImages[1], combo1_key_scale, new AnchorLayoutParams(combo1_key_X, combo1_key_Y, NONE, NONE, true))); - - if (g_Config.bShowComboKey2) - root->Add(new ComboKey(g_Config.iCombokey2, roundImage, comboKeyImages[2], combo2_key_scale, new AnchorLayoutParams(combo2_key_X, combo2_key_Y, NONE, NONE, true))); - - if (g_Config.bShowComboKey3) - root->Add(new ComboKey(g_Config.iCombokey3, roundImage, comboKeyImages[3], combo3_key_scale, new AnchorLayoutParams(combo3_key_X, combo3_key_Y, NONE, NONE, true))); - - if (g_Config.bShowComboKey4) - root->Add(new ComboKey(g_Config.iCombokey4, roundImage, comboKeyImages[4], combo4_key_scale, new AnchorLayoutParams(combo4_key_X, combo4_key_Y, NONE, NONE, true))); + if (!System_GetPropertyBool(SYSPROP_HAS_BACK_BUTTON) || g_Config.bShowTouchPause) { + root->Add(new BoolButton(pause, roundImage, I_ARROW, 1.0f, new AnchorLayoutParams(halfW, 20, NONE, NONE, true)))->SetAngle(90); } + // touchActionButtonCenter.show will always be true, since that's the default. + if (g_Config.bShowTouchCircle) + addPSPButton(CTRL_CIRCLE, roundImage, I_CIRCLE, g_Config.touchActionButtonCenter, circleOffset); + if (g_Config.bShowTouchCross) + addPSPButton(CTRL_CROSS, roundImage, I_CROSS, g_Config.touchActionButtonCenter, crossOffset); + if (g_Config.bShowTouchTriangle) + addPSPButton(CTRL_TRIANGLE, roundImage, I_TRIANGLE, g_Config.touchActionButtonCenter, triangleOffset); + if (g_Config.bShowTouchSquare) + addPSPButton(CTRL_SQUARE, roundImage, I_SQUARE, g_Config.touchActionButtonCenter, squareOffset); + + addPSPButton(CTRL_START, rectImage, I_START, g_Config.touchStartKey); + addPSPButton(CTRL_SELECT, rectImage, I_SELECT, g_Config.touchSelectKey); + + BoolButton *unthrottle = addBoolButton(&PSP_CoreParameter().unthrottle, rectImage, I_ARROW, g_Config.touchUnthrottleKey); + if (unthrottle) + unthrottle->SetAngle(180); + + addPSPButton(CTRL_LTRIGGER, shoulderImage, I_L, g_Config.touchLKey); + PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, shoulderImage, I_R, g_Config.touchRKey); + if (rTrigger) + rTrigger->FlipImageH(true); + + if (g_Config.touchDpad.show) + root->Add(new PSPDpad(dirImage, I_ARROW, g_Config.touchDpad.scale, g_Config.fDpadSpacing, buttonLayoutParams(g_Config.touchDpad))); + + if (g_Config.touchAnalogStick.show) + root->Add(new PSPStick(stickBg, stickImage, 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick))); + + addComboKey(g_Config.iCombokey0, roundImage, comboKeyImages[0], g_Config.touchCombo0); + addComboKey(g_Config.iCombokey1, roundImage, comboKeyImages[1], g_Config.touchCombo1); + addComboKey(g_Config.iCombokey2, roundImage, comboKeyImages[2], g_Config.touchCombo2); + addComboKey(g_Config.iCombokey3, roundImage, comboKeyImages[3], g_Config.touchCombo3); + addComboKey(g_Config.iCombokey4, roundImage, comboKeyImages[4], g_Config.touchCombo4); + return root; } diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 87dee45e31..a248b01f9e 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -42,9 +42,9 @@ static u32 GetButtonColor() { class DragDropButton : public MultiTouchButton { public: - DragDropButton(float &x, float &y, int bgImg, int img, float &scale) - : MultiTouchButton(bgImg, img, scale, new UI::AnchorLayoutParams(fromFullscreenCoord(x), y*local_dp_yres, UI::NONE, UI::NONE, true)), - x_(x), y_(y), theScale_(scale) { + DragDropButton(ConfigTouchPos &pos, int bgImg, int img) + : MultiTouchButton(bgImg, img, pos.scale, new UI::AnchorLayoutParams(fromFullscreenCoord(pos.x), pos.y * local_dp_yres, UI::NONE, UI::NONE, true)), + x_(pos.x), y_(pos.y), theScale_(pos.scale) { scale_ = theScale_; } @@ -89,8 +89,8 @@ private: class PSPActionButtons : public DragDropButton { public: - PSPActionButtons(float &x, float &y, float &scale, float &spacing) - : DragDropButton(x, y, -1, -1, scale), spacing_(spacing) { + PSPActionButtons(ConfigTouchPos &pos, float &spacing) + : DragDropButton(pos, -1, -1), spacing_(spacing) { using namespace UI; roundId_ = g_Config.iTouchButtonStyle ? I_ROUND_LINE : I_ROUND; @@ -172,8 +172,8 @@ private: class PSPDPadButtons : public DragDropButton { public: - PSPDPadButtons(float &x, float &y, float &scale, float &spacing) - : DragDropButton(x, y, -1, -1, scale), spacing_(spacing) { + PSPDPadButtons(ConfigTouchPos &pos, float &spacing) + : DragDropButton(pos, -1, -1), spacing_(spacing) { } void Draw(UIContext &dc) override { @@ -361,7 +361,7 @@ void TouchControlLayoutScreen::CreateViews() { controls_.clear(); - PSPActionButtons *actionButtons = new PSPActionButtons(g_Config.fActionButtonCenterX, g_Config.fActionButtonCenterY, g_Config.fActionButtonScale, g_Config.fActionButtonSpacing); + PSPActionButtons *actionButtons = new PSPActionButtons(g_Config.touchActionButtonCenter, g_Config.fActionButtonSpacing); actionButtons->setCircleVisibility(g_Config.bShowTouchCircle); actionButtons->setCrossVisibility(g_Config.bShowTouchCross); actionButtons->setTriangleVisibility(g_Config.bShowTouchTriangle); @@ -378,51 +378,51 @@ void TouchControlLayoutScreen::CreateViews() { const int comboKeyImages[5] = { I_1, I_2, I_3, I_4, I_5 }; - if (g_Config.bShowTouchDpad) { - controls_.push_back(new PSPDPadButtons(g_Config.fDpadX, g_Config.fDpadY, g_Config.fDpadScale, g_Config.fDpadSpacing)); + if (g_Config.touchDpad.show) { + controls_.push_back(new PSPDPadButtons(g_Config.touchDpad, g_Config.fDpadSpacing)); } - if (g_Config.bShowTouchSelect) { - controls_.push_back(new DragDropButton(g_Config.fSelectKeyX, g_Config.fSelectKeyY, rectImage, I_SELECT, g_Config.fSelectKeyScale)); + if (g_Config.touchSelectKey.show) { + controls_.push_back(new DragDropButton(g_Config.touchSelectKey, rectImage, I_SELECT)); } - if (g_Config.bShowTouchStart) { - controls_.push_back(new DragDropButton(g_Config.fStartKeyX, g_Config.fStartKeyY, rectImage, I_START, g_Config.fStartKeyScale)); + if (g_Config.touchStartKey.show) { + controls_.push_back(new DragDropButton(g_Config.touchStartKey, rectImage, I_START)); } - if (g_Config.bShowTouchUnthrottle) { - DragDropButton *unthrottle = new DragDropButton(g_Config.fUnthrottleKeyX, g_Config.fUnthrottleKeyY, rectImage, I_ARROW, g_Config.fUnthrottleKeyScale); + if (g_Config.touchUnthrottleKey.show) { + DragDropButton *unthrottle = new DragDropButton(g_Config.touchUnthrottleKey, rectImage, I_ARROW); unthrottle->SetAngle(180.0f); controls_.push_back(unthrottle); } - if (g_Config.bShowTouchLTrigger) { - controls_.push_back(new DragDropButton(g_Config.fLKeyX, g_Config.fLKeyY, shoulderImage, I_L, g_Config.fLKeyScale)); + if (g_Config.touchLKey.show) { + controls_.push_back(new DragDropButton(g_Config.touchLKey, shoulderImage, I_L)); } - if (g_Config.bShowTouchRTrigger) { - DragDropButton *rbutton = new DragDropButton(g_Config.fRKeyX, g_Config.fRKeyY, shoulderImage, I_R, g_Config.fRKeyScale); + if (g_Config.touchRKey.show) { + DragDropButton *rbutton = new DragDropButton(g_Config.touchRKey, shoulderImage, I_R); rbutton->FlipImageH(true); controls_.push_back(rbutton); } - if (g_Config.bShowTouchAnalogStick) { - controls_.push_back(new DragDropButton(g_Config.fAnalogStickX, g_Config.fAnalogStickY, stickBg, stickImage, g_Config.fAnalogStickScale)); + if (g_Config.touchAnalogStick.show) { + controls_.push_back(new DragDropButton(g_Config.touchAnalogStick, stickBg, stickImage)); } - if (g_Config.bShowComboKey0) { - controls_.push_back(new DragDropButton(g_Config.fcombo0X, g_Config.fcombo0Y, roundImage, comboKeyImages[0], g_Config.fcomboScale0)); + if (g_Config.touchCombo0.show) { + controls_.push_back(new DragDropButton(g_Config.touchCombo0, roundImage, comboKeyImages[0])); } - if (g_Config.bShowComboKey1) { - controls_.push_back(new DragDropButton(g_Config.fcombo1X, g_Config.fcombo1Y, roundImage, comboKeyImages[1], g_Config.fcomboScale1)); + if (g_Config.touchCombo1.show) { + controls_.push_back(new DragDropButton(g_Config.touchCombo1, roundImage, comboKeyImages[1])); } - if (g_Config.bShowComboKey2) { - controls_.push_back(new DragDropButton(g_Config.fcombo2X, g_Config.fcombo2Y, roundImage, comboKeyImages[2], g_Config.fcomboScale2)); + if (g_Config.touchCombo2.show) { + controls_.push_back(new DragDropButton(g_Config.touchCombo2, roundImage, comboKeyImages[2])); } - if (g_Config.bShowComboKey3) { - controls_.push_back(new DragDropButton(g_Config.fcombo3X, g_Config.fcombo3Y, roundImage, comboKeyImages[3], g_Config.fcomboScale3)); + if (g_Config.touchCombo3.show) { + controls_.push_back(new DragDropButton(g_Config.touchCombo3, roundImage, comboKeyImages[3])); } - if (g_Config.bShowComboKey4) { - controls_.push_back(new DragDropButton(g_Config.fcombo4X, g_Config.fcombo4Y, roundImage, comboKeyImages[4], g_Config.fcomboScale4)); + if (g_Config.touchCombo4.show) { + controls_.push_back(new DragDropButton(g_Config.touchCombo4, roundImage, comboKeyImages[4])); }; for (size_t i = 0; i < controls_.size(); i++) { diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 2d0381ede5..a4fd18c9e9 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -62,18 +62,18 @@ void TouchControlVisibilityScreen::CreateViews() { keyToggles["Cross"] = &g_Config.bShowTouchCross; keyToggles["Square"] = &g_Config.bShowTouchSquare; keyToggles["Triangle"] = &g_Config.bShowTouchTriangle; - keyToggles["L"] = &g_Config.bShowTouchLTrigger; - keyToggles["R"] = &g_Config.bShowTouchRTrigger; - keyToggles["Start"] = &g_Config.bShowTouchStart; - keyToggles["Select"] = &g_Config.bShowTouchSelect; - keyToggles["Dpad"] = &g_Config.bShowTouchDpad; - keyToggles["Analog Stick"] = &g_Config.bShowTouchAnalogStick; - keyToggles["Unthrottle"] = &g_Config.bShowTouchUnthrottle; - keyToggles["Combo0"] = &g_Config.bShowComboKey0; - keyToggles["Combo1"] = &g_Config.bShowComboKey1; - keyToggles["Combo2"] = &g_Config.bShowComboKey2; - keyToggles["Combo3"] = &g_Config.bShowComboKey3; - keyToggles["Combo4"] = &g_Config.bShowComboKey4; + keyToggles["L"] = &g_Config.touchLKey.show; + keyToggles["R"] = &g_Config.touchRKey.show; + keyToggles["Start"] = &g_Config.touchStartKey.show; + keyToggles["Select"] = &g_Config.touchSelectKey.show; + keyToggles["Dpad"] = &g_Config.touchDpad.show; + keyToggles["Analog Stick"] = &g_Config.touchAnalogStick.show; + keyToggles["Unthrottle"] = &g_Config.touchUnthrottleKey.show; + keyToggles["Combo0"] = &g_Config.touchCombo0.show; + keyToggles["Combo1"] = &g_Config.touchCombo1.show; + keyToggles["Combo2"] = &g_Config.touchCombo2.show; + keyToggles["Combo3"] = &g_Config.touchCombo3.show; + keyToggles["Combo4"] = &g_Config.touchCombo4.show; std::map::iterator imageFinder; From f2956a1c1a2de5507e05a33f5c3a89a4f7d3f72b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 23:00:21 -0700 Subject: [PATCH 266/301] UI: Show custom speed toggle buttons. This allows more flexibility if unthrottle is too fast or too uneven. --- Core/Config.cpp | 4 +++ Core/Config.h | 2 ++ UI/GamepadEmu.cpp | 40 +++++++++++++++++++++++++++-- UI/GamepadEmu.h | 26 +++++++++++++++---- UI/TouchControlLayoutScreen.cpp | 12 +++++++++ UI/TouchControlVisibilityScreen.cpp | 2 ++ 6 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index a0cd76f37f..4d6061a49f 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -723,6 +723,8 @@ static ConfigSetting controlSettings[] = { ConfigSetting("fcombo2X", "fcombo2Y", "comboKeyScale2", "ShowComboKey2", &g_Config.touchCombo2, defaultTouchPosHide, true, true), ConfigSetting("fcombo3X", "fcombo3Y", "comboKeyScale3", "ShowComboKey3", &g_Config.touchCombo3, defaultTouchPosHide, true, true), ConfigSetting("fcombo4X", "fcombo4Y", "comboKeyScale4", "ShowComboKey4", &g_Config.touchCombo4, defaultTouchPosHide, true, true), + ConfigSetting("Speed1KeyX", "Speed1KeyY", "Speed1KeyScale", "ShowSpeed1Key", &g_Config.touchSpeed1Key, defaultTouchPosHide, true, true), + ConfigSetting("Speed2KeyX", "Speed2KeyY", "Speed2KeyScale", "ShowSpeed2Key", &g_Config.touchSpeed2Key, defaultTouchPosHide, true, true), #ifdef _WIN32 ConfigSetting("DInputAnalogDeadzone", &g_Config.fDInputAnalogDeadzone, 0.1f, true, true), @@ -1428,6 +1430,8 @@ void Config::ResetControlLayout() { reset(g_Config.touchCombo2); reset(g_Config.touchCombo3); reset(g_Config.touchCombo4); + reset(g_Config.touchSpeed1Key); + reset(g_Config.touchSpeed2Key); } void Config::GetReportingInfo(UrlEncoder &data) { diff --git a/Core/Config.h b/Core/Config.h index 60d12010de..9223cfde7a 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -294,6 +294,8 @@ public: ConfigTouchPos touchCombo2; ConfigTouchPos touchCombo3; ConfigTouchPos touchCombo4; + ConfigTouchPos touchSpeed1Key; + ConfigTouchPos touchSpeed2Key; // Controls Visibility bool bShowTouchControls; diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index a0de9fa829..909f765ef7 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -107,7 +107,7 @@ void MultiTouchButton::Draw(UIContext &dc) { uint32_t colorBg = colorAlpha(GetButtonColor(), opacity); uint32_t color = colorAlpha(0xFFFFFF, opacity); - dc.Draw()->DrawImageRotated(bgImg_, bounds_.centerX(), bounds_.centerY(), scale, angle_ * (M_PI * 2 / 360.0f), colorBg, flipImageH_); + dc.Draw()->DrawImageRotated(bgImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), colorBg, flipImageH_); int y = bounds_.centerY(); // Hack round the fact that the center of the rectangular picture the triangle is contained in @@ -127,6 +127,26 @@ void BoolButton::Touch(const TouchInput &input) { } } +void FPSLimitButton::Touch(const TouchInput &input) { + bool lastDown = pointerDownMask_ != 0; + MultiTouchButton::Touch(input); + bool down = pointerDownMask_ != 0; + + if (!down && lastDown && IsDown()) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; + } else if (down && !lastDown && PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { + int limit = limit_ == FPSLimit::CUSTOM1 ? g_Config.iFpsLimit1 : g_Config.iFpsLimit2; + // Validate it actually has a setting (may this should override visible?) + if (limit >= 0) { + PSP_CoreParameter().fpsLimit = limit_; + } + } +} + +bool FPSLimitButton::IsDown() { + return PSP_CoreParameter().fpsLimit == limit_; +} + void PSPButton::Touch(const TouchInput &input) { bool lastDown = pointerDownMask_ != 0; MultiTouchButton::Touch(input); @@ -445,6 +465,9 @@ void InitPadLayout(float xres, float yres, float globalScale) { int unthrottle_key_Y = yres - 60 * scale; initTouchPos(g_Config.touchUnthrottleKey, unthrottle_key_X, unthrottle_key_Y); + initTouchPos(g_Config.touchSpeed1Key, unthrottle_key_X, unthrottle_key_Y - 60 * scale); + initTouchPos(g_Config.touchSpeed2Key, unthrottle_key_X + bottom_key_spacing * scale, unthrottle_key_Y - 60 * scale); + // L and R------------------------------------------------------------ // Put them above the analog stick / above the buttons to the right. // The corners were very hard to reach.. @@ -532,6 +555,12 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { } return nullptr; }; + auto addFPSLimitButton = [=](FPSLimit value, int bgImg, int img, const ConfigTouchPos &touch) -> FPSLimitButton * { + if (touch.show) { + return root->Add(new FPSLimitButton(value, bgImg, img, touch.scale, buttonLayoutParams(touch))); + } + return nullptr; + }; if (!System_GetPropertyBool(SYSPROP_HAS_BACK_BUTTON) || g_Config.bShowTouchPause) { root->Add(new BoolButton(pause, roundImage, I_ARROW, 1.0f, new AnchorLayoutParams(halfW, 20, NONE, NONE, true)))->SetAngle(90); @@ -552,7 +581,14 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { BoolButton *unthrottle = addBoolButton(&PSP_CoreParameter().unthrottle, rectImage, I_ARROW, g_Config.touchUnthrottleKey); if (unthrottle) - unthrottle->SetAngle(180); + unthrottle->SetAngle(180.0f); + + FPSLimitButton *speed1 = addFPSLimitButton(FPSLimit::CUSTOM1, rectImage, I_ARROW, g_Config.touchSpeed1Key); + if (speed1) + speed1->SetAngle(170.0f, 180.0f); + FPSLimitButton *speed2 = addFPSLimitButton(FPSLimit::CUSTOM2, rectImage, I_ARROW, g_Config.touchSpeed2Key); + if (speed2) + speed2->SetAngle(190.0f, 180.0f); addPSPButton(CTRL_LTRIGGER, shoulderImage, I_L, g_Config.touchLKey); PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, shoulderImage, I_R, g_Config.touchRKey); diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index 79e72bcf43..283c093ec0 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -22,6 +22,7 @@ #include "ui/view.h" #include "ui/viewgroup.h" +#include "Core/CoreParameter.h" class GamepadView : public UI::View { public: @@ -43,7 +44,7 @@ protected: class MultiTouchButton : public GamepadView { public: MultiTouchButton(int bgImg, int img, float scale, UI::LayoutParams *layoutParams) - : GamepadView(layoutParams), pointerDownMask_(0), scale_(scale), bgImg_(bgImg), img_(img), angle_(0.0f), flipImageH_(false) { + : GamepadView(layoutParams), scale_(scale), bgImg_(bgImg), img_(img) { } void Touch(const TouchInput &input) override; @@ -52,17 +53,19 @@ public: virtual bool IsDown() { return pointerDownMask_ != 0; } // chainable MultiTouchButton *FlipImageH(bool flip) { flipImageH_ = flip; return this; } - MultiTouchButton *SetAngle(float angle) { angle_ = angle; return this; } + MultiTouchButton *SetAngle(float angle) { angle_ = angle; bgAngle_ = angle; return this; } + MultiTouchButton *SetAngle(float angle, float bgAngle) { angle_ = angle; bgAngle_ = bgAngle; return this; } protected: - uint32_t pointerDownMask_; + uint32_t pointerDownMask_ = 0; float scale_; private: int bgImg_; int img_; - float angle_; - bool flipImageH_; + float bgAngle_ = 0.0f; + float angle_ = 0.0f; + bool flipImageH_ = false; }; class BoolButton : public MultiTouchButton { @@ -78,6 +81,19 @@ private: bool *value_; }; +class FPSLimitButton : public MultiTouchButton { +public: + FPSLimitButton(FPSLimit limit, int bgImg, int img, float scale, UI::LayoutParams *layoutParams) + : MultiTouchButton(bgImg, img, scale, layoutParams), limit_(limit) { + + } + void Touch(const TouchInput &input) override; + bool IsDown() override; + +private: + FPSLimit limit_; +}; + class PSPButton : public MultiTouchButton { public: PSPButton(int pspButtonBit, int bgImg, int img, float scale, UI::LayoutParams *layoutParams) diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index a248b01f9e..fc841f4e8c 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -396,6 +396,18 @@ void TouchControlLayoutScreen::CreateViews() { controls_.push_back(unthrottle); } + if (g_Config.touchSpeed1Key.show) { + DragDropButton *speed1 = new DragDropButton(g_Config.touchSpeed1Key, rectImage, I_ARROW); + speed1->SetAngle(170.0f, 180.0f); + controls_.push_back(speed1); + } + + if (g_Config.touchSpeed2Key.show) { + DragDropButton *speed2 = new DragDropButton(g_Config.touchSpeed2Key, rectImage, I_ARROW); + speed2->SetAngle(190.0f, 180.0f); + controls_.push_back(speed2); + } + if (g_Config.touchLKey.show) { controls_.push_back(new DragDropButton(g_Config.touchLKey, shoulderImage, I_L)); } diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index a4fd18c9e9..c370bebee5 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -74,6 +74,8 @@ void TouchControlVisibilityScreen::CreateViews() { keyToggles["Combo2"] = &g_Config.touchCombo2.show; keyToggles["Combo3"] = &g_Config.touchCombo3.show; keyToggles["Combo4"] = &g_Config.touchCombo4.show; + keyToggles["Alt speed 1"] = &g_Config.touchSpeed1Key.show; + keyToggles["Alt speed 2"] = &g_Config.touchSpeed2Key.show; std::map::iterator imageFinder; From 70e5ae4fadb31ca8b2e709abb1f06df34c559e8b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 23:11:56 -0700 Subject: [PATCH 267/301] UI: Sort touch control visibility better. --- UI/TouchControlVisibilityScreen.cpp | 74 +++++++++++------------------ UI/TouchControlVisibilityScreen.h | 14 +++--- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index c370bebee5..64fc8cb301 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -42,59 +42,40 @@ void TouchControlVisibilityScreen::CreateViews() { gridsettings.fillCells = true; GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT))); - std::map keyImages; - keyImages["Circle"] = I_CIRCLE; - keyImages["Cross"] = I_CROSS; - keyImages["Square"] = I_SQUARE; - keyImages["Triangle"] = I_TRIANGLE; - keyImages["Start"] = I_START; - keyImages["Select"] = I_SELECT; - keyImages["L"] = I_L; - keyImages["R"] = I_R; - keyImages["Combo0"] = I_1; - keyImages["Combo1"] = I_2; - keyImages["Combo2"] = I_3; - keyImages["Combo3"] = I_4; - keyImages["Combo4"] = I_5; - - keyToggles.clear(); - keyToggles["Circle"] = &g_Config.bShowTouchCircle; - keyToggles["Cross"] = &g_Config.bShowTouchCross; - keyToggles["Square"] = &g_Config.bShowTouchSquare; - keyToggles["Triangle"] = &g_Config.bShowTouchTriangle; - keyToggles["L"] = &g_Config.touchLKey.show; - keyToggles["R"] = &g_Config.touchRKey.show; - keyToggles["Start"] = &g_Config.touchStartKey.show; - keyToggles["Select"] = &g_Config.touchSelectKey.show; - keyToggles["Dpad"] = &g_Config.touchDpad.show; - keyToggles["Analog Stick"] = &g_Config.touchAnalogStick.show; - keyToggles["Unthrottle"] = &g_Config.touchUnthrottleKey.show; - keyToggles["Combo0"] = &g_Config.touchCombo0.show; - keyToggles["Combo1"] = &g_Config.touchCombo1.show; - keyToggles["Combo2"] = &g_Config.touchCombo2.show; - keyToggles["Combo3"] = &g_Config.touchCombo3.show; - keyToggles["Combo4"] = &g_Config.touchCombo4.show; - keyToggles["Alt speed 1"] = &g_Config.touchSpeed1Key.show; - keyToggles["Alt speed 2"] = &g_Config.touchSpeed2Key.show; - - std::map::iterator imageFinder; + toggles_.clear(); + toggles_.push_back({ "Circle", &g_Config.bShowTouchCircle, I_CIRCLE }); + toggles_.push_back({ "Cross", &g_Config.bShowTouchCross, I_CROSS }); + toggles_.push_back({ "Square", &g_Config.bShowTouchSquare, I_SQUARE }); + toggles_.push_back({ "Triangle", &g_Config.bShowTouchTriangle, I_TRIANGLE }); + toggles_.push_back({ "L", &g_Config.touchLKey.show, I_L }); + toggles_.push_back({ "R", &g_Config.touchRKey.show, I_R }); + toggles_.push_back({ "Start", &g_Config.touchStartKey.show, I_START }); + toggles_.push_back({ "Select", &g_Config.touchSelectKey.show, I_SELECT }); + toggles_.push_back({ "Dpad", &g_Config.touchDpad.show, -1 }); + toggles_.push_back({ "Analog Stick", &g_Config.touchAnalogStick.show, -1 }); + toggles_.push_back({ "Unthrottle", &g_Config.touchUnthrottleKey.show, -1 }); + toggles_.push_back({ "Combo0", &g_Config.touchCombo0.show, I_1 }); + toggles_.push_back({ "Combo1", &g_Config.touchCombo1.show, I_2 }); + toggles_.push_back({ "Combo2", &g_Config.touchCombo2.show, I_3 }); + toggles_.push_back({ "Combo3", &g_Config.touchCombo3.show, I_4 }); + toggles_.push_back({ "Combo4", &g_Config.touchCombo4.show, I_5 }); + toggles_.push_back({ "Alt speed 1", &g_Config.touchSpeed1Key.show, -1 }); + toggles_.push_back({ "Alt speed 2", &g_Config.touchSpeed2Key.show, -1 }); I18NCategory *mc = GetI18NCategory("MappableControls"); - for (auto i = keyToggles.begin(); i != keyToggles.end(); ++i) { + for (auto toggle : toggles_) { LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); row->SetSpacing(0); - CheckBox *checkbox = new CheckBox(i->second, "", "", new LinearLayoutParams(50, WRAP_CONTENT)); + CheckBox *checkbox = new CheckBox(toggle.show, "", "", new LinearLayoutParams(50, WRAP_CONTENT)); row->Add(checkbox); - imageFinder = keyImages.find(i->first); Choice *choice; - - if (imageFinder != keyImages.end()) { - choice = new Choice(keyImages[imageFinder->first], new LinearLayoutParams(1.0f)); + if (toggle.img != -1) { + choice = new Choice(toggle.img, new LinearLayoutParams(1.0f)); } else { - choice = new Choice(mc->T(i->first.c_str()), new LinearLayoutParams(1.0f)); + choice = new Choice(mc->T(toggle.key), new LinearLayoutParams(1.0f)); } ChoiceEventHandler *choiceEventHandler = new ChoiceEventHandler(checkbox); @@ -112,11 +93,10 @@ void TouchControlVisibilityScreen::onFinish(DialogResult result) { } UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) { - for (auto i = keyToggles.begin(); i != keyToggles.end(); ++i) { - *i->second = toggleSwitch; + for (auto toggle : toggles_) { + *toggle.show = nextToggleAll_; } - - toggleSwitch = !toggleSwitch; + nextToggleAll_ = !nextToggleAll_; return UI::EVENT_DONE; } diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index d7f98885f0..e702a5f6aa 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -19,16 +19,18 @@ #include "MiscScreens.h" -#include - namespace UI { class CheckBox; } +struct TouchButtonToggle { + const char *key; + bool *show; + int img; +}; + class TouchControlVisibilityScreen : public UIDialogScreenWithBackground { public: - TouchControlVisibilityScreen() : toggleSwitch(true){ } - void CreateViews() override; void onFinish(DialogResult result) override; @@ -36,8 +38,8 @@ protected: UI::EventReturn OnToggleAll(UI::EventParams &e); private: - std::map keyToggles; - bool toggleSwitch; + std::vector toggles_; + bool nextToggleAll_ = true; class ChoiceEventHandler{ public: From 0b0d4c1edf02eb33607afea2bfc65f8bab0389da Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 23:25:46 -0700 Subject: [PATCH 268/301] UI: Make touch control visibility more consistent. Now it looks more like other screens. --- UI/TouchControlVisibilityScreen.cpp | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 64fc8cb301..1d0e99e553 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -20,23 +20,33 @@ #include "UI/ui_atlas.h" #include "i18n/i18n.h" +static const int leftColumnWidth = 140; + void TouchControlVisibilityScreen::CreateViews() { using namespace UI; - root_ = new ScrollView(ORIENT_VERTICAL); - LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); + I18NCategory *di = GetI18NCategory("Dialog"); + I18NCategory *co = GetI18NCategory("Controls"); + + root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + + Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 10)); + root_->Add(back)->OnClick.Handle(this, &UIScreen::OnBack); + Choice *toggleAll = new Choice(di->T("Toggle All"), "", false, new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 84)); + root_->Add(toggleAll)->OnClick.Handle(this, &TouchControlVisibilityScreen::OnToggleAll); + + TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, new AnchorLayoutParams(10, 0, 10, 0, false)); + tabHolder->SetTag("TouchControlVisibility"); + root_->Add(tabHolder); + ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL); + tabHolder->AddTab(co->T("Visibility"), rightPanel); + + LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); vert->SetSpacing(0); - LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL); - I18NCategory *di = GetI18NCategory("Dialog"); - topBar->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); - topBar->Add(new Choice(di->T("Toggle All")))->OnClick.Handle(this, &TouchControlVisibilityScreen::OnToggleAll); - - vert->Add(topBar); - I18NCategory *co = GetI18NCategory("Controls"); vert->Add(new ItemHeader(co->T("Touch Control Visibility"))); - const int cellSize = 400; + const int cellSize = 380; UI::GridLayoutSettings gridsettings(cellSize, 64, 5); gridsettings.fillCells = true; From c50f5cc3db2eb419ba0e2a01a2c5a2f865261952 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 16 Jun 2018 23:32:34 -0700 Subject: [PATCH 269/301] UI: Fix leak in control visibility screen. --- UI/TouchControlVisibilityScreen.cpp | 26 ++++++++++++++++++++------ UI/TouchControlVisibilityScreen.h | 8 -------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 1d0e99e553..4e67d1cc5c 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -22,6 +22,23 @@ static const int leftColumnWidth = 140; +class CheckBoxChoice : public UI::Choice { +public: + CheckBoxChoice(const std::string &text, UI::CheckBox *checkbox, UI::LayoutParams *lp) + : Choice(text, lp), checkbox_(checkbox) { + OnClick.Handle(this, &CheckBoxChoice::HandleClick); + } + CheckBoxChoice(ImageID imgID, UI::CheckBox *checkbox, UI::LayoutParams *lp) + : Choice(imgID, lp), checkbox_(checkbox) { + OnClick.Handle(this, &CheckBoxChoice::HandleClick); + } + +private: + UI::EventReturn HandleClick(UI::EventParams &e); + + UI::CheckBox *checkbox_; +}; + void TouchControlVisibilityScreen::CreateViews() { using namespace UI; @@ -83,14 +100,11 @@ void TouchControlVisibilityScreen::CreateViews() { Choice *choice; if (toggle.img != -1) { - choice = new Choice(toggle.img, new LinearLayoutParams(1.0f)); + choice = new CheckBoxChoice(toggle.img, checkbox, new LinearLayoutParams(1.0f)); } else { - choice = new Choice(mc->T(toggle.key), new LinearLayoutParams(1.0f)); + choice = new CheckBoxChoice(mc->T(toggle.key), checkbox, new LinearLayoutParams(1.0f)); } - ChoiceEventHandler *choiceEventHandler = new ChoiceEventHandler(checkbox); - choice->OnClick.Handle(choiceEventHandler, &ChoiceEventHandler::onChoiceClick); - choice->SetCentered(true); row->Add(choice); @@ -111,7 +125,7 @@ UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) { return UI::EVENT_DONE; } -UI::EventReturn TouchControlVisibilityScreen::ChoiceEventHandler::onChoiceClick(UI::EventParams &e){ +UI::EventReturn CheckBoxChoice::HandleClick(UI::EventParams &e) { checkbox_->Toggle(); return UI::EVENT_DONE; diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index e702a5f6aa..88455a343e 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -40,12 +40,4 @@ protected: private: std::vector toggles_; bool nextToggleAll_ = true; - - class ChoiceEventHandler{ - public: - ChoiceEventHandler(UI::CheckBox *checkbox) : checkbox_(checkbox) {} - UI::EventReturn onChoiceClick(UI::EventParams &e); - private: - UI::CheckBox *checkbox_; - }; }; From 0564e9e3ecbbbef9da406c964bb0ab7aa36b220f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 23 Jun 2018 11:06:56 -0700 Subject: [PATCH 270/301] UI: Disable stepping on unthrottle touch button. --- UI/GamepadEmu.cpp | 13 ++++++++++++- UI/GamepadEmu.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 909f765ef7..96253b2727 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -18,6 +18,7 @@ #include #include "Common/Log.h" #include "Core/Config.h" +#include "Core/Core.h" #include "Core/System.h" #include "Core/HLE/sceCtrl.h" #include "UI/GamepadEmu.h" @@ -124,6 +125,9 @@ void BoolButton::Touch(const TouchInput &input) { if (down != lastDown) { *value_ = down; + UI::EventParams params{ this }; + params.a = down; + OnChange.Trigger(params); } } @@ -580,8 +584,15 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { addPSPButton(CTRL_SELECT, rectImage, I_SELECT, g_Config.touchSelectKey); BoolButton *unthrottle = addBoolButton(&PSP_CoreParameter().unthrottle, rectImage, I_ARROW, g_Config.touchUnthrottleKey); - if (unthrottle) + if (unthrottle) { unthrottle->SetAngle(180.0f); + unthrottle->OnChange.Add([](UI::EventParams &e) { + if (e.a) { + Core_EnableStepping(false); + } + return UI::EVENT_DONE; + }); + } FPSLimitButton *speed1 = addFPSLimitButton(FPSLimit::CUSTOM1, rectImage, I_ARROW, g_Config.touchSpeed1Key); if (speed1) diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index 283c093ec0..087fa7da8d 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -77,6 +77,8 @@ public: void Touch(const TouchInput &input) override; bool IsDown() override { return *value_; } + UI::Event OnChange; + private: bool *value_; }; From ca8677d262a2a32d2608d985eae8d83c8b47d58a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 23 Jun 2018 10:58:30 -0700 Subject: [PATCH 271/301] UI: Keep rendering UI even while stepping. This makes it possible to "get out" on mobile. --- Core/Core.cpp | 15 ++++++++++----- Core/System.cpp | 4 ++++ UI/EmuScreen.cpp | 9 +++++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 51e2fcaab4..32db6ab451 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -225,7 +225,7 @@ void Core_RunLoop(GraphicsContext *ctx) { } } - while (!coreState && GetUIState() == UISTATE_INGAME) { + while ((coreState == CORE_RUNNING || coreState == CORE_STEPPING) && GetUIState() == UISTATE_INGAME) { time_update(); UpdateRunLoop(); if (!windowHidden && !Core_IsStepping()) { @@ -264,8 +264,9 @@ void Core_SingleStep() { static inline bool Core_WaitStepping() { std::unique_lock guard(m_hStepMutex); + // We only wait 16ms so that we can still draw UI or react to events. if (!singleStepPending && coreState == CORE_STEPPING) - m_StepCond.wait(guard); + m_StepCond.wait_for(guard, std::chrono::milliseconds(16)); bool result = singleStepPending; singleStepPending = false; @@ -285,9 +286,13 @@ void Core_ProcessStepping() { GPUStepping::SingleStep(); // We're not inside jit now, so it's safe to clear the breakpoints. - CBreakPoints::ClearTemporaryBreakPoints(); - host->UpdateDisassembly(); - host->UpdateMemView(); + static int lastSteppingCounter = -1; + if (lastSteppingCounter != steppingCounter) { + CBreakPoints::ClearTemporaryBreakPoints(); + host->UpdateDisassembly(); + host->UpdateMemView(); + lastSteppingCounter = steppingCounter; + } // Need to check inside the lock to avoid races. bool doStep = Core_WaitStepping(); diff --git a/Core/System.cpp b/Core/System.cpp index b59a365872..bd55510b06 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -442,6 +442,10 @@ void PSP_RunLoopWhileState() { // Run until CORE_NEXTFRAME while (coreState == CORE_RUNNING || coreState == CORE_STEPPING) { PSP_RunLoopFor(blockTicks); + if (coreState == CORE_STEPPING) { + // Keep the UI responsive. + break; + } } } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 7dcedfa43d..9d38a4b859 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1230,8 +1230,13 @@ void EmuScreen::render() { // set back to running for the next frame coreState = CORE_RUNNING; } else if (coreState == CORE_STEPPING) { - // If we're stepping, it's convenient not to clear the screen. - thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }); + // If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output. + // This won't work in non-buffered, but that's fine. + thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }); + // Just to make sure. + if (PSP_IsInited()) { + gpu->CopyDisplayToOutput(); + } } else { // Didn't actually reach the end of the frame, ran out of the blockTicks cycles. // In this case we need to bind and wipe the backbuffer, at least. From 5028486630989fdecc89ab453bafb1a92b5e0c81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Jun 2018 07:34:07 -0700 Subject: [PATCH 272/301] thin3d: Cleanup some unused/uninitialized warnings. --- ext/native/base/logging.h | 1 - ext/native/thin3d/GLQueueRunner.cpp | 7 ++++--- ext/native/thin3d/GLRenderManager.cpp | 4 +++- ext/native/thin3d/VulkanQueueRunner.cpp | 7 ++----- ext/native/thin3d/VulkanQueueRunner.h | 1 - ext/native/thin3d/VulkanRenderManager.cpp | 6 ++---- ext/native/thin3d/thin3d_gl.cpp | 3 --- ext/native/thin3d/thin3d_vulkan.cpp | 1 - 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/ext/native/base/logging.h b/ext/native/base/logging.h index 41e6ab519a..0e5ac59def 100644 --- a/ext/native/base/logging.h +++ b/ext/native/base/logging.h @@ -102,7 +102,6 @@ const char *GetFn(const char *fn); #define ELOG(...) XLOG_IMPL("E", __VA_ARGS__) #define FLOG(...) do {XLOG_IMPL("F", __VA_ARGS__); Crash();} while (false) -// TODO: Win32 version using OutputDebugString #else #include diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 6f7ccc917d..49406aea8b 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -240,7 +240,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } case GLRInitStepType::CREATE_INPUT_LAYOUT: { - GLRInputLayout *layout = step.create_input_layout.inputLayout; + // GLRInputLayout *layout = step.create_input_layout.inputLayout; // Nothing to do unless we want to create vertexbuffer objects (GL 4.5) break; } @@ -537,7 +537,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glBindVertexArray(globalVAO_); } - GLRFramebuffer *fb = step.render.framebuffer; GLRProgram *curProgram = nullptr; int activeSlot = 0; glActiveTexture(GL_TEXTURE0 + activeSlot); @@ -547,7 +546,6 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { int colorMask = -1; int depthMask = -1; int depthFunc = -1; - int logicOp = -1; GLuint curArrayBuffer = (GLuint)-1; GLuint curElemArrayBuffer = (GLuint)-1; bool depthEnabled = false; @@ -555,7 +553,10 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { bool blendEnabled = false; bool cullEnabled = false; bool ditherEnabled = false; +#ifndef USING_GLES2 + int logicOp = -1; bool logicEnabled = false; +#endif GLuint blendEqColor = (GLuint)-1; GLuint blendEqAlpha = (GLuint)-1; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 1e986b4195..2a73695d46 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -14,9 +14,11 @@ #endif static std::thread::id renderThreadId; +#if MAX_LOGLEVEL >= DEBUG_LEVEL static bool OnRenderThread() { return std::this_thread::get_id() == renderThreadId; } +#endif // Runs on the GPU thread. void GLDeleter::Perform(GLRenderManager *renderManager) { @@ -698,7 +700,7 @@ void GLPushBuffer::NextBuffer(size_t minSize) { } void GLPushBuffer::Defragment() { - _dbg_assert_(G3D, std::this_thread::get_id() != renderThreadId); + _dbg_assert_(G3D, !OnRenderThread()); if (buffers_.size() <= 1) { // Let's take this chance to jetison localMemory we don't need. diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 7b6d54b0cf..d8d4e69f65 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -64,7 +64,6 @@ void VulkanQueueRunner::ResizeReadbackBuffer(VkDeviceSize requiredSize) { void VulkanQueueRunner::DestroyDeviceObjects() { ILOG("VulkanQueueRunner::DestroyDeviceObjects"); - VkDevice device = vulkan_->GetDevice(); vulkan_->Delete().QueueDeleteDeviceMemory(readbackMemory_); vulkan_->Delete().QueueDeleteBuffer(readbackBuffer_); readbackBufferSize_ = 0; @@ -675,9 +674,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c barrier.subresourceRange.layerCount = 1; barrier.subresourceRange.levelCount = 1; barrier.image = iter.fb->color.image; - barrier.srcAccessMask = 0; - VkPipelineStageFlags srcStage; - VkPipelineStageFlags dstStage; + VkPipelineStageFlags srcStage{}; + VkPipelineStageFlags dstStage{}; switch (barrier.oldLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; @@ -729,7 +727,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VKRFramebuffer *fb = step.render.framebuffer; VkPipeline lastPipeline = VK_NULL_HANDLE; - VkDescriptorSet lastDescSet = VK_NULL_HANDLE; auto &commands = step.commands; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 5acfb22d6b..c01873f99a 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -248,7 +248,6 @@ private: VkFramebuffer backbuffer_; VkImage backbufferImage_; - VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE; VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE; VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 48ad0b3d25..4f2b24ead2 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -91,7 +91,7 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int break; default: Crash(); - break; + return; } TransitionImageLayout2(cmd, img.image, 0, 1, aspects, @@ -263,7 +263,6 @@ void VulkanRenderManager::DestroyBackbuffers() { StopThread(); vulkan_->WaitUntilQueueIdle(); - VkDevice device = vulkan_->GetDevice(); for (uint32_t i = 0; i < swapchainImageCount_; i++) { vulkan_->Delete().QueueDeleteImageView(swapchainImages_[i].view); } @@ -289,7 +288,6 @@ VulkanRenderManager::~VulkanRenderManager() { vkDestroySemaphore(device, acquireSemaphore_, nullptr); vkDestroySemaphore(device, renderingCompleteSemaphore_, nullptr); for (int i = 0; i < vulkan_->GetInflightFrames(); i++) { - VkCommandBuffer cmdBuf[2]{ frameData_[i].mainCmd, frameData_[i].initCmd }; vkFreeCommandBuffers(device, frameData_[i].cmdPoolInit, 1, &frameData_[i].initCmd); vkFreeCommandBuffers(device, frameData_[i].cmdPoolMain, 1, &frameData_[i].mainCmd); vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); @@ -450,7 +448,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int a FlushSync(); - Draw::DataFormat srcFormat; + Draw::DataFormat srcFormat = Draw::DataFormat::UNDEFINED; if (aspectBits & VK_IMAGE_ASPECT_COLOR_BIT) { if (src) { switch (src->color.format) { diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 63e8bb809d..0952366dd4 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -260,7 +260,6 @@ private: ShaderLanguage language_; GLRShader *shader_ = nullptr; GLuint glstage_ = 0; - bool ok_ = false; std::string source_; // So we can recompile in case of context loss. }; @@ -1039,8 +1038,6 @@ OpenGLInputLayout::~OpenGLInputLayout() { } void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { - int semMask = 0; - // TODO: This is only accurate if there's only one stream. But whatever, for now we // never use multiple streams anyway. stride = (GLsizei)desc.bindings[0].stride; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 26bf8206c9..d44f2f8e51 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -707,7 +707,6 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur vkTex_ = new VulkanTexture(vulkan_, alloc); vkTex_->SetTag(desc.tag); VkFormat vulkanFormat = DataFormatToVulkan(format_); - int stride = desc.width * (int)DataFormatSizeInBytes(format_); int bpp = GetBpp(vulkanFormat); int bytesPerPixel = bpp / 8; int usageBits = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; From 81f74e4287c3e7e05f2c549509207e28ce0028e2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Jun 2018 07:35:19 -0700 Subject: [PATCH 273/301] Vulkan: Assert on some creates failing. Before was only checking in debug builds. --- ext/native/thin3d/GLRenderManager.cpp | 4 ++-- ext/native/thin3d/VulkanQueueRunner.cpp | 19 +++++++++++++++---- ext/native/thin3d/VulkanRenderManager.cpp | 4 ++-- ext/native/thin3d/thin3d_vulkan.cpp | 8 ++++---- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 2a73695d46..75de166d37 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -578,7 +578,7 @@ void GLRenderManager::WaitUntilQueueIdle() { GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), target_(target), size_(size) { bool res = AddBuffer(); - assert(res); + _assert_(res); } GLPushBuffer::~GLPushBuffer() { @@ -720,7 +720,7 @@ void GLPushBuffer::Defragment() { size_ = newSize; bool res = AddBuffer(); - assert(res); + _assert_(res); } size_t GLPushBuffer::GetTotalSize() const { diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index d8d4e69f65..2e122de544 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -55,7 +55,7 @@ void VulkanQueueRunner::ResizeReadbackBuffer(VkDeviceSize requiredSize) { VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &alloc.memoryTypeIndex); - assert(success); + _assert_(success); vkAllocateMemory(device, &alloc, nullptr, &readbackMemory_); uint32_t offset = 0; @@ -149,7 +149,7 @@ void VulkanQueueRunner::InitBackbufferRenderPass() { rp_info.pDependencies = &dep; VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_); - assert(res == VK_SUCCESS); + _assert_(res == VK_SUCCESS); } VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { @@ -317,6 +317,9 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: // Nothing to do. break; + default: + _dbg_assert_msg_(G3D, false, "GetRenderPass: Unexpected final color layout %d", (int)key.finalColorLayout); + break; } if (deps[numDeps].dstAccessMask) { @@ -462,6 +465,8 @@ void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { if (steps[j]->copy.dst != steps[i]->copy.dst) last = j - 1; break; + default: + break; } if (last != -1) break; @@ -534,6 +539,9 @@ void VulkanQueueRunner::ApplySonicHack(std::vector &steps) { if (steps[j]->render.numDraws != 3 && steps[j]->render.numDraws != 6) last = j - 1; } + break; + default: + break; } if (last != -1) break; @@ -643,6 +651,9 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass) { case VKRRenderCommand::PUSH_CONSTANTS: ILOG(" PushConstants(%d)", cmd.push.size); break; + + case VKRRenderCommand::NUM_RENDER_COMMANDS: + break; } } ILOG("RenderPass End(%x)", fb); @@ -690,7 +701,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; break; default: - Crash(); + _assert_msg_(G3D, false, "PerformRenderPass: Unexpected oldLayout: %d", (int)barrier.oldLayout); break; } barrier.newLayout = iter.targetLayout; @@ -700,7 +711,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; default: - Crash(); + _assert_msg_(G3D, false, "PerformRenderPass: Unexpected newLayout: %d", (int)barrier.newLayout); break; } barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 4f2b24ead2..9a788b1468 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -384,7 +384,7 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() { VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; VkResult res = vkBeginCommandBuffer(frameData.initCmd, &begin); - assert(res == VK_SUCCESS); + _assert_(res == VK_SUCCESS); frameData.hasInitCommands = true; } return frameData_[curFrame].initCmd; @@ -1008,7 +1008,7 @@ void VulkanRenderManager::EndSyncFrame(int frame) { VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; VkResult res = vkBeginCommandBuffer(frameData.mainCmd, &begin); - assert(res == VK_SUCCESS); + _assert_(res == VK_SUCCESS); if (useThread_) { std::unique_lock lock(frameData.push_mutex); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index d44f2f8e51..8f6906d839 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -661,7 +661,7 @@ public: s.mipmapMode = desc.mipFilter == TextureFilter::LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST; s.maxLod = desc.maxLod; VkResult res = vkCreateSampler(vulkan_->GetDevice(), &s, nullptr, &sampler_); - assert(VK_SUCCESS == res); + _assert_(VK_SUCCESS == res); } ~VKSamplerState() { vulkan_->Delete().QueueDeleteSampler(sampler_); @@ -788,7 +788,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024); VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool); - assert(res == VK_SUCCESS); + _assert_(res == VK_SUCCESS); } // binding 0 - uniform data @@ -862,7 +862,7 @@ void VKContext::BeginFrame() { frame.descSets_.clear(); VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); - assert(result == VK_SUCCESS); + _assert_(result == VK_SUCCESS); } void VKContext::WaitRenderCompletion(Framebuffer *fbo) { @@ -903,7 +903,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { alloc.pSetLayouts = &descriptorSetLayout_; alloc.descriptorSetCount = 1; VkResult res = vkAllocateDescriptorSets(device_, &alloc, &descSet); - assert(VK_SUCCESS == res); + _assert_(VK_SUCCESS == res); VkDescriptorBufferInfo bufferDesc; bufferDesc.buffer = buf; From aad6377580cfe05d7c521c2cad2df34e946f3c2a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Jun 2018 13:28:57 -0700 Subject: [PATCH 274/301] Savedata: Return result codes from Save/Load. This doesn't change anything, yet. --- Core/Dialog/PSPSaveDialog.cpp | 64 ++++++++++++++++------------------- Core/Dialog/SavedataParam.cpp | 33 ++++++++---------- Core/Dialog/SavedataParam.h | 6 ++-- 3 files changed, 47 insertions(+), 56 deletions(-) diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index a37184a45f..cb68396f6a 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -1019,14 +1019,14 @@ void PSPSaveDialog::ExecuteIOAction() { std::lock_guard guard(paramLock); switch (display) { case DS_LOAD_LOADING: - if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave)) { + if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave) == 0) { display = DS_LOAD_DONE; } else { display = DS_LOAD_FAILED; } break; case DS_SAVE_SAVING: - if (param.Save(param.GetPspParam(), GetSelectedSaveDirName())) { + if (param.Save(param.GetPspParam(), GetSelectedSaveDirName()) == 0) { display = DS_SAVE_DONE; } else { display = DS_SAVE_FAILED; @@ -1052,87 +1052,81 @@ void PSPSaveDialog::ExecuteIOAction() { } void PSPSaveDialog::ExecuteNotVisibleIOAction() { + auto &result = param.GetPspParam()->common.result; + switch ((SceUtilitySavedataType)(u32)param.GetPspParam()->mode) { case SCE_UTILITY_SAVEDATA_TYPE_LOAD: // Only load and exit case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD: - if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave)) { - param.GetPspParam()->common.result = 0; - } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA; - } + result = param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave); break; case SCE_UTILITY_SAVEDATA_TYPE_SAVE: // Only save and exit case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE: - if (param.Save(param.GetPspParam(), GetSelectedSaveDirName())) { - param.GetPspParam()->common.result = 0; - } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE; - } + result = param.Save(param.GetPspParam(), GetSelectedSaveDirName()); break; case SCE_UTILITY_SAVEDATA_TYPE_SIZES: - param.GetPspParam()->common.result = param.GetSizes(param.GetPspParam()); + result = param.GetSizes(param.GetPspParam()); break; case SCE_UTILITY_SAVEDATA_TYPE_LIST: param.GetList(param.GetPspParam()); - param.GetPspParam()->common.result = 0; + result = 0; break; case SCE_UTILITY_SAVEDATA_TYPE_FILES: - param.GetPspParam()->common.result = param.GetFilesList(param.GetPspParam()); + result = param.GetFilesList(param.GetPspParam()); break; case SCE_UTILITY_SAVEDATA_TYPE_GETSIZE: { - bool result = param.GetSize(param.GetPspParam()); + bool sizeResult = param.GetSize(param.GetPspParam()); // TODO: According to JPCSP, should test/verify this part but seems edge casey. if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK; - } else if (result) { - param.GetPspParam()->common.result = 0; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK; + } else if (sizeResult) { + result = 0; } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; } } break; case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA: DEBUG_LOG(SCEUTILITY, "sceUtilitySavedata DELETEDATA: %s", param.GetPspParam()->saveName); - param.GetPspParam()->common.result = param.DeleteData(param.GetPspParam()); + result = param.DeleteData(param.GetPspParam()); break; //case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE: case SCE_UTILITY_SAVEDATA_TYPE_SINGLEDELETE: if (param.Delete(param.GetPspParam(), param.GetSelectedSave())) { - param.GetPspParam()->common.result = 0; + result = 0; } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA; + result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA; } break; // TODO: Should reset the directory's other files. case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA: case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE: - if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE)) { - param.GetPspParam()->common.result = 0; + if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE) == 0) { + result = 0; } else if (MemoryStick_FreeSpace() == 0) { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL; } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; } break; case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA: case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE: - if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE)) { - param.GetPspParam()->common.result = 0; + if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE) == 0) { + result = 0; } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; } break; case SCE_UTILITY_SAVEDATA_TYPE_READDATA: case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE: if (!param.IsSaveDirectoryExist(param.GetPspParam())){ - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; } else if (!param.IsSfoFileExist(param.GetPspParam())) { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN; - } else if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave, param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE)) { - param.GetPspParam()->common.result = 0; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN; + } else if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave, param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE) == 0) { + result = 0; } else { - param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND; + result = SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND; } break; default: diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 9c33e4e04e..ef68f15602 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -358,9 +358,9 @@ int SavedataParam::DeleteData(SceUtilitySavedataParam* param) { return 0; } -bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveDirName, bool secureMode) { +int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveDirName, bool secureMode) { if (!param) { - return false; + return SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE; } std::string dirPath = GetSaveFilePath(param, GetSaveDir(param, saveDirName)); @@ -502,11 +502,8 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save if (!WritePSPFile(filePath, data_, saveSize)) { ERROR_LOG(SCEUTILITY,"Error writing file %s",filePath.c_str()); - if(cryptedData != 0) - { - delete[] cryptedData; - } - return false; + delete[] cryptedData; + return SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE; } delete[] cryptedData; } @@ -538,23 +535,23 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save WritePSPFile(snd0path, param->snd0FileData.buf, param->snd0FileData.bufSize); } - return true; + return 0; } -bool SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &saveDirName, int saveId, bool secureMode) -{ +int SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &saveDirName, int saveId, bool secureMode) { if (!param) { - return false; + return SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA; } std::string dirPath = GetSaveFilePath(param, GetSaveDir(param, saveDirName)); std::string filePath = dirPath + "/" + GetFileName(param); if (!pspFileSystem.GetFileInfo(filePath).exists) { - return false; + return SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA; } - if(!LoadSaveData(param, saveDirName, dirPath, secureMode)) // Load main savedata - return false; + int result = LoadSaveData(param, saveDirName, dirPath, secureMode); + if (result != 0) + return result; LoadSFO(param, dirPath); // Load sfo @@ -572,10 +569,10 @@ bool SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &save // Load SND0.AT3 LoadFile(dirPath, SND0_FILENAME, ¶m->snd0FileData); - return true; + return 0; } -bool SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode) { +int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string &dirPath, bool secureMode) { if (param->secureVersion != 0) { WARN_LOG_REPORT(SCEUTILITY, "Savedata version requested: %d", param->secureVersion); } @@ -587,7 +584,7 @@ bool SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::stri int saveSize = -1; if (!ReadPSPFile(filePath, &saveData, saveSize, &readSize)) { ERROR_LOG(SCEUTILITY,"Error reading file %s",filePath.c_str()); - return false; + return SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA; } saveSize = (int)readSize; @@ -606,7 +603,7 @@ bool SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::stri param->dataSize = (SceSize)saveSize; delete[] saveData; - return true; + return 0; } int SavedataParam::DetermineCryptMode(const SceUtilitySavedataParam *param) const { diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 0ea0e4ae61..167f57be51 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -306,8 +306,8 @@ public: std::string GetSaveDir(const SceUtilitySavedataParam *param, const std::string &saveDirName) const; bool Delete(SceUtilitySavedataParam* param, int saveId = -1); int DeleteData(SceUtilitySavedataParam* param); - bool Save(SceUtilitySavedataParam* param, const std::string &saveDirName, bool secureMode = true); - bool Load(SceUtilitySavedataParam* param, const std::string &saveDirName, int saveId = -1, bool secureMode = true); + int Save(SceUtilitySavedataParam* param, const std::string &saveDirName, bool secureMode = true); + int Load(SceUtilitySavedataParam* param, const std::string &saveDirName, int saveId = -1, bool secureMode = true); int GetSizes(SceUtilitySavedataParam* param); bool GetList(SceUtilitySavedataParam* param); int GetFilesList(SceUtilitySavedataParam* param); @@ -354,7 +354,7 @@ private: void SetFileInfo(SaveFileInfo &saveInfo, PSPFileInfo &info, std::string saveName); void ClearFileInfo(SaveFileInfo &saveInfo, const std::string &saveName); - bool LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode); + int LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode); void LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, bool &saveDone); void LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize); void LoadSFO(SceUtilitySavedataParam *param, const std::string& dirPath); From 46d7ead78132d57536b361271d527ebd0ec6cc95 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Jun 2018 15:30:33 -0700 Subject: [PATCH 275/301] Savedata: Add validation on secureVersion param. --- Core/Dialog/SavedataParam.cpp | 19 ++++++++++++++++++- Core/Dialog/SavedataParam.h | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index ef68f15602..df35edc461 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -362,6 +362,16 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD if (!param) { return SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE; } + if (param->secureVersion > 3) { + ERROR_LOG_REPORT(SCEUTILITY, "Savedata version requested on save: %d", param->secureVersion); + return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM; + } else if (param->secureVersion != 0) { + if (param->secureVersion != 1 && !HasKey(param)) { + ERROR_LOG_REPORT(SCEUTILITY, "Savedata version with missing key on save: %d", param->secureVersion); + return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM; + } + WARN_LOG_REPORT(SCEUTILITY, "Savedata version requested on save: %d", param->secureVersion); + } std::string dirPath = GetSaveFilePath(param, GetSaveDir(param, saveDirName)); @@ -573,7 +583,14 @@ int SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &saveD } int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string &dirPath, bool secureMode) { - if (param->secureVersion != 0) { + if (param->secureVersion > 3) { + ERROR_LOG_REPORT(SCEUTILITY, "Savedata version requested: %d", param->secureVersion); + return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM; + } else if (param->secureVersion != 0) { + if (param->secureVersion != 1 && !HasKey(param)) { + ERROR_LOG_REPORT(SCEUTILITY, "Savedata version with missing key: %d", param->secureVersion); + return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM; + } WARN_LOG_REPORT(SCEUTILITY, "Savedata version requested: %d", param->secureVersion); } u8 *data_ = param->dataBuf; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 167f57be51..f4779a5256 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -253,7 +253,7 @@ struct SceUtilitySavedataParam u8 key[16]; - s32_le secureVersion; + u32_le secureVersion; s32_le multiStatus; // Function 11 LIST From 93d96b8a3217710a32a7f8a35620d8d252cdc3f2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Jun 2018 15:34:27 -0700 Subject: [PATCH 276/301] Savedata: Map secureVersion modes correctly. 1 was correct, but other modes were not. --- Core/Dialog/SavedataParam.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index df35edc461..7fd7d8a489 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -400,8 +400,13 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD memcpy(cryptedData, data_, cryptedSize); int decryptMode = DetermineCryptMode(param); - if (EncryptData(decryptMode, cryptedData, &cryptedSize, &aligned_len, cryptedHash, (HasKey(param) ? param->key : 0)) != 0) - { + bool hasKey = decryptMode > 1; + if (hasKey && !HasKey(param)) { + delete[] cryptedData; + return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM; + } + + if (EncryptData(decryptMode, cryptedData, &cryptedSize, &aligned_len, cryptedHash, (hasKey ? param->key : 0)) != 0) { I18NCategory *err = GetI18NCategory("Error"); host->NotifyUserMessage(err->T("Save encryption failed. This save won't work on real PSP"), 6.0f); ERROR_LOG(SCEUTILITY,"Save encryption failed. This save won't work on real PSP"); @@ -612,7 +617,10 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin bool isCrypted = prevCryptMode != 0 && secureMode; bool saveDone = false; if (isCrypted) { + if (DetermineCryptMode(param) > 1 && !HasKey(param)) + return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM; LoadCryptedSave(param, data_, saveData, saveSize, prevCryptMode, saveDone); + // TODO: Should return SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN here if !saveDone. } if (!saveDone) { LoadNotCryptedSave(param, data_, saveData, saveSize); @@ -625,9 +633,14 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin int SavedataParam::DetermineCryptMode(const SceUtilitySavedataParam *param) const { int decryptMode = 1; - if (param->secureVersion != 0) { - decryptMode = param->secureVersion; + if (param->secureVersion == 1) { + decryptMode = 1; + } else if (param->secureVersion == 2) { + decryptMode = 3; + } else if (param->secureVersion == 3) { + decryptMode = GetSDKMainVersion(sceKernelGetCompiledSdkVersion()) >= 4 ? 5 : 1; } else if (HasKey(param)) { + // TODO: This should ignore HasKey(), which would trigger errors. Not doing that yet to play it safe. decryptMode = GetSDKMainVersion(sceKernelGetCompiledSdkVersion()) >= 4 ? 5 : 3; } return decryptMode; From ecdc4bbd1716423a50991072c9e9db7ff551b015 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Jun 2018 15:46:41 -0700 Subject: [PATCH 277/301] Savedata: Upgrade secureVersion by default. This may help fix save data made incorrectly, but may also break save data last saved in PPSSPP v0.9.8 or lower. Can be toggled off otherwise. --- Core/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 4d6061a49f..2c6b5f67d3 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -797,7 +797,7 @@ static ConfigSetting systemParamSettings[] = { #endif ConfigSetting("WlanPowerSave", &g_Config.bWlanPowerSave, (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, true, true), ReportedConfigSetting("EncryptSave", &g_Config.bEncryptSave, true, true, true), - ConfigSetting("SavedataUpgrade", &g_Config.bSavedataUpgrade, false, true, false), + ConfigSetting("SavedataUpgradeVersion", &g_Config.bSavedataUpgrade, true, true, false), ConfigSetting(false), }; From 5536cdd82700fbc342120054e0df72d89ff0ff6a Mon Sep 17 00:00:00 2001 From: xebra Date: Tue, 26 Jun 2018 01:47:13 +0900 Subject: [PATCH 278/301] [spline/bezier]Fix tex coords. --- GPU/GLES/DrawEngineGLES.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 2d84693fc5..3880edc83b 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -673,7 +673,7 @@ void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float if (data_tex[1]) renderManager_->DeleteTexture(data_tex[1]); uint8_t *tex_data = new uint8_t[size * sizeof(float) * 4]; - memcpy(tex_data, pos, size * sizeof(float) * 4); + memcpy(tex_data, tex, size * sizeof(float) * 4); data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D); renderManager_->TextureImage(data_tex[1], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, tex_data, GLRAllocType::NEW, false); renderManager_->FinalizeTexture(data_tex[1], 0, false); From 7fd7afeba98c1bc814796196daff639ec0ccad44 Mon Sep 17 00:00:00 2001 From: hissingshark Date: Thu, 21 Jun 2018 09:00:57 +0100 Subject: [PATCH 279/301] Added "oldest save" and "slots 1-5" as options for "auto load savestate" --- Core/Config.cpp | 2 +- Core/Config.h | 2 +- Core/ConfigValues.h | 6 ++++++ Core/SaveState.cpp | 38 ++++++++++++++++++++++++++++++++++++++ Core/SaveState.h | 5 +++-- UI/EmuScreen.cpp | 23 +++++++++++++++++++---- UI/GameSettingsScreen.cpp | 4 ++-- 7 files changed, 70 insertions(+), 10 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 2c6b5f67d3..e7c22fe0ad 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -398,7 +398,7 @@ static ConfigSetting generalSettings[] = { ConfigSetting("ForceLagSync", &g_Config.bForceLagSync, false, true, true), ReportedConfigSetting("NumWorkerThreads", &g_Config.iNumWorkerThreads, &DefaultNumWorkers, true, true), - ConfigSetting("EnableAutoLoad", &g_Config.bEnableAutoLoad, false, true, true), + ConfigSetting("AutoLoadSaveState", &g_Config.iAutoLoadSaveState, 0, true, true), ReportedConfigSetting("EnableCheats", &g_Config.bEnableCheats, false, true, true), ConfigSetting("CwCheatRefreshRate", &g_Config.iCwCheatRefreshRate, 77, true, true), diff --git a/Core/Config.h b/Core/Config.h index 9223cfde7a..d39957e7fb 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -175,7 +175,7 @@ public: int iCurrentStateSlot; int iRewindFlipFrequency; bool bEnableStateUndo; - bool bEnableAutoLoad; + int iAutoLoadSaveState; // 0 = off, 1 = oldest, 2 = newest, >2 = slot number + 3 bool bEnableCheats; bool bReloadCheats; int iCwCheatRefreshRate; diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index bc2df61f37..4074b3e758 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -71,3 +71,9 @@ enum class SmallDisplayZoom { AUTO = 2, MANUAL = 3, }; + +enum AutoLoadSaveState { + OFF = 0, + OLDEST = 1, + NEWEST = 2, +}; diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 7d336dd993..bb39f8304e 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -550,6 +550,27 @@ namespace SaveState return false; } + bool operator > (const tm &t1, const tm &t2) { + if (t1.tm_year > t2.tm_year) return true; + if (t1.tm_year < t2.tm_year) return false; + if (t1.tm_mon > t2.tm_mon) return true; + if (t1.tm_mon < t2.tm_mon) return false; + if (t1.tm_mday > t2.tm_mday) return true; + if (t1.tm_mday < t2.tm_mday) return false; + if (t1.tm_hour > t2.tm_hour) return true; + if (t1.tm_hour < t2.tm_hour) return false; + if (t1.tm_min > t2.tm_min) return true; + if (t1.tm_min < t2.tm_min) return false; + if (t1.tm_sec > t2.tm_sec) return true; + if (t1.tm_sec < t2.tm_sec) return false; + return false; + } + + bool operator ! (const tm &t1) { + if (t1.tm_year || t1.tm_mon || t1.tm_mday || t1.tm_hour || t1.tm_min || t1.tm_sec) return false; + return true; + } + int GetNewestSlot(const std::string &gameFilename) { int newestSlot = -1; tm newestDate = {0}; @@ -567,6 +588,23 @@ namespace SaveState return newestSlot; } + int GetOldestSlot(const std::string &gameFilename) { + int oldestSlot = -1; + tm oldestDate = {0}; + for (int i = 0; i < NUM_SLOTS; i++) { + std::string fn = GenerateSaveSlotFilename(gameFilename, i, STATE_EXTENSION); + if (File::Exists(fn)) { + tm time; + bool success = File::GetModifTime(fn, time); + if (success && (!oldestDate || oldestDate > time)) { + oldestDate = time; + oldestSlot = i; + } + } + } + return oldestSlot; + } + std::string GetSlotDateAsString(const std::string &gameFilename, int slot) { std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION); if (File::Exists(fn)) { diff --git a/Core/SaveState.h b/Core/SaveState.h index ec9efe7bb5..e190e6e65d 100644 --- a/Core/SaveState.h +++ b/Core/SaveState.h @@ -51,9 +51,10 @@ namespace SaveState int GetCurrentSlot(); - // Returns -1 if there's no newest slot. + // Returns -1 if there's no oldest/newest slot. int GetNewestSlot(const std::string &gameFilename); - + int GetOldestSlot(const std::string &gameFilename); + std::string GetSlotDateAsString(const std::string &gameFilename, int slot); std::string GenerateSaveSlotFilename(const std::string &gameFilename, int slot, const char *extension); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 9d38a4b859..8c4814857a 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1337,11 +1337,26 @@ void EmuScreen::renderUI() { } void EmuScreen::autoLoad() { + int autoSlot = -1; + //check if save state has save, if so, load - int lastSlot = SaveState::GetNewestSlot(gamePath_); - if (g_Config.bEnableAutoLoad && lastSlot != -1) { - SaveState::LoadSlot(gamePath_, lastSlot, &AfterSaveStateAction); - g_Config.iCurrentStateSlot = lastSlot; + switch (g_Config.iAutoLoadSaveState) { + case AutoLoadSaveState::OFF: // "AutoLoad Off" + return; + case AutoLoadSaveState::OLDEST: // "Oldest Save" + autoSlot = SaveState::GetOldestSlot(gamePath_); + break; + case AutoLoadSaveState::NEWEST: // "Newest Save" + autoSlot = SaveState::GetNewestSlot(gamePath_); + break; + default: // try the specific save state slot specified + autoSlot = (SaveState::HasSaveInSlot(gamePath_, g_Config.iAutoLoadSaveState - 3)) ? (g_Config.iAutoLoadSaveState - 3) : -1; + break; + } + + if (g_Config.iAutoLoadSaveState && autoSlot != -1) { + SaveState::LoadSlot(gamePath_, autoSlot, &AfterSaveStateAction); + g_Config.iCurrentStateSlot = autoSlot; } } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 6e76db39e7..8ccec4b5e7 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -734,8 +734,8 @@ void GameSettingsScreen::CreateViews() { systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings); systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups"))); - systemSettings->Add(new CheckBox(&g_Config.bEnableAutoLoad, sy->T("Auto Load Newest Savestate"))); - + static const char *autoLoadSaveStateChoices[] = { "Off", "Oldest Save", "Newest Save", "Slot 1", "Slot 2", "Slot 3", "Slot 4", "Slot 5" }; + systemSettings->Add(new PopupMultiChoice(&g_Config.iAutoLoadSaveState, sy->T("Auto Load Savestate"), autoLoadSaveStateChoices, 0, ARRAY_SIZE(autoLoadSaveStateChoices), sy->GetName(), screenManager())); #if defined(USING_WIN_UI) systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Enable Windows native keyboard", "Enable Windows native keyboard"))); #endif From 84faf9d8d4ef89db29af333c37e81fc8a7d1263d Mon Sep 17 00:00:00 2001 From: TacoTheDank Date: Mon, 25 Jun 2018 21:57:23 -0400 Subject: [PATCH 280/301] Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b6cb00b4fc..b32f870af1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.2' + classpath 'com.android.tools.build:gradle:3.1.3' } } From 52f97a4b3ab876f2790df58934cf88c033826fd4 Mon Sep 17 00:00:00 2001 From: TacoTheDank Date: Mon, 25 Jun 2018 22:00:09 -0400 Subject: [PATCH 281/301] Update gradle-wrapper.properties --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a54cb8423f..95c68256f8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip From 087f542878234b7f4be3ddb1013d02aef69c23d5 Mon Sep 17 00:00:00 2001 From: TacoTheDank Date: Mon, 25 Jun 2018 22:04:42 -0400 Subject: [PATCH 282/301] Update build.gradle updated sdk version --- android/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 183f19d423..55bf308f5d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.gladed.androidgitversion' version '0.4.3' + id 'com.gladed.androidgitversion' version '0.4.4' } apply plugin: 'com.android.application' @@ -34,8 +34,8 @@ android { } } } - compileSdkVersion 26 - buildToolsVersion '27.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.0' defaultConfig { applicationId 'org.ppsspp.ppsspp' if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) { @@ -51,7 +51,7 @@ android { new File("versioncode.txt").write(androidGitVersion.code().toString()) minSdkVersion 9 - targetSdkVersion 26 + targetSdkVersion 28 if (project.hasProperty("ANDROID_VERSION_CODE") && project.hasProperty("ANDROID_VERSION_NAME")) { versionCode ANDROID_VERSION_CODE versionName ANDROID_VERSION_NAME From 822e65e1ed7d6c89a666a43f58d83e7076f02f14 Mon Sep 17 00:00:00 2001 From: TacoTheDank Date: Mon, 25 Jun 2018 22:05:17 -0400 Subject: [PATCH 283/301] Update AndroidManifest.xml --- android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 9af9bd0fd6..7c2ca632bb 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -8,7 +8,7 @@ + android:targetSdkVersion="28" /> From 64e432e12de3667f70fefe6af16c27c7c355a34d Mon Sep 17 00:00:00 2001 From: hissingshark Date: Tue, 26 Jun 2018 07:52:02 +0100 Subject: [PATCH 284/301] Moved from using enum to enum class --- Core/ConfigValues.h | 2 +- UI/EmuScreen.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 4074b3e758..e714fa9b0c 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -72,7 +72,7 @@ enum class SmallDisplayZoom { MANUAL = 3, }; -enum AutoLoadSaveState { +enum class AutoLoadSaveState { OFF = 0, OLDEST = 1, NEWEST = 2, diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 8c4814857a..b4e761e980 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1341,12 +1341,12 @@ void EmuScreen::autoLoad() { //check if save state has save, if so, load switch (g_Config.iAutoLoadSaveState) { - case AutoLoadSaveState::OFF: // "AutoLoad Off" + case (int)AutoLoadSaveState::OFF: // "AutoLoad Off" return; - case AutoLoadSaveState::OLDEST: // "Oldest Save" + case (int)AutoLoadSaveState::OLDEST: // "Oldest Save" autoSlot = SaveState::GetOldestSlot(gamePath_); break; - case AutoLoadSaveState::NEWEST: // "Newest Save" + case (int)AutoLoadSaveState::NEWEST: // "Newest Save" autoSlot = SaveState::GetNewestSlot(gamePath_); break; default: // try the specific save state slot specified From 4fab160e1978b634479a4a59973cb6a0889d5512 Mon Sep 17 00:00:00 2001 From: xebra Date: Mon, 29 Jan 2018 14:14:57 +0900 Subject: [PATCH 285/301] [spline/bezier]Modify D3D11 hwtess texture buffer to structured shader buffer. --- GPU/D3D11/DrawEngineD3D11.cpp | 89 +++++++---------------- GPU/D3D11/DrawEngineD3D11.h | 28 ++++--- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 23 +++--- 3 files changed, 53 insertions(+), 87 deletions(-) diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 2e09aee228..be7ffd9e83 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -695,71 +695,38 @@ rotateVBO: #endif } -void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { - // Position +void DrawEngineD3D11::TessellationDataTransferD3D11::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) { + struct TessData { + float pos[3]; float pad1; + float uv[2]; float pad2[2]; + float color[4]; + }; + if (prevSize < size) { prevSize = size; - if (data_tex[0]) { - data_tex[0]->Release(); - view[0]->Release(); + if (buf) { + buf->Release(); + view->Release(); } - desc.Width = size; - desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; - HRESULT hr = device_->CreateTexture1D(&desc, nullptr, &data_tex[0]); - if (FAILED(hr)) { - INFO_LOG(G3D, "Failed to create D3D texture for HW tessellation"); - data_tex[0]->Release(); - return; // TODO: Turn off HW tessellation if texture creation error occured. - } - hr = device_->CreateShaderResourceView(data_tex[0], nullptr, &view[0]); - ASSERT_SUCCESS(hr); - context_->VSSetShaderResources(0, 1, &view[0]); - } - dstBox.right = size; - context_->UpdateSubresource(data_tex[0], 0, &dstBox, pos, 0, 0); + desc.ByteWidth = size * sizeof(TessData); + desc.StructureByteStride = sizeof(TessData); - // Texcoords - if (hasTexCoords) { - if (prevSizeTex < size) { - prevSizeTex = size; - if (data_tex[1]) { - data_tex[1]->Release(); - view[1]->Release(); - } - desc.Width = size; - desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; - HRESULT hr = device_->CreateTexture1D(&desc, nullptr, &data_tex[1]); - if (FAILED(hr)) { - INFO_LOG(G3D, "Failed to create D3D texture for HW tessellation"); - data_tex[1]->Release(); - return; - } - hr = device_->CreateShaderResourceView(data_tex[1], nullptr, &view[1]); - context_->VSSetShaderResources(1, 1, &view[1]); - } - dstBox.right = size; - context_->UpdateSubresource(data_tex[1], 0, &dstBox, tex, 0, 0); + device_->CreateBuffer(&desc, nullptr, &buf); + device_->CreateShaderResourceView(buf, 0, &view); + context_->VSSetShaderResources(0, 1, &view); } + D3D11_MAPPED_SUBRESOURCE map; + context_->Map(buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + uint8_t *data = (uint8_t *)map.pData; - // Color - int sizeColor = hasColor ? size : 1; - if (prevSizeCol < sizeColor) { - prevSizeCol = sizeColor; - if (data_tex[2]) { - data_tex[2]->Release(); - view[2]->Release(); - } - desc.Width = sizeColor; - desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; - HRESULT hr = device_->CreateTexture1D(&desc, nullptr, &data_tex[2]); - if (FAILED(hr)) { - INFO_LOG(G3D, "Failed to create D3D texture for HW tessellation"); - data_tex[2]->Release(); - return; - } - hr = device_->CreateShaderResourceView(data_tex[2], nullptr, &view[2]); - context_->VSSetShaderResources(2, 1, &view[2]); - } - dstBox.right = sizeColor; - context_->UpdateSubresource(data_tex[2], 0, &dstBox, col, 0, 0); + pos = (float *)(data); + tex = (float *)(data + offsetof(TessData, uv)); + col = (float *)(data + offsetof(TessData, color)); + posStride = sizeof(TessData) / sizeof(float); + colStride = hasColor ? (sizeof(TessData) / sizeof(float)) : 0; + texStride = sizeof(TessData) / sizeof(float); +} + +void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { + context_->Unmap(buf, 0); } diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 9d4589baae..8b797a3d2d 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -203,27 +203,25 @@ private: private: ID3D11DeviceContext *context_; ID3D11Device *device_; - ID3D11Texture1D *data_tex[3]; - ID3D11ShaderResourceView *view[3]; - D3D11_TEXTURE1D_DESC desc; - D3D11_BOX dstBox; + ID3D11Buffer *buf; + ID3D11ShaderResourceView *view; + D3D11_BUFFER_DESC desc; public: - TessellationDataTransferD3D11(ID3D11DeviceContext *context_, ID3D11Device *device_) - : TessellationDataTransfer(), context_(context_), device_(device_), data_tex(), view(), desc(), dstBox{0, 0, 0, 1, 1, 1} { - desc.CPUAccessFlags = 0; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.ArraySize = 1; - desc.MipLevels = 1; + TessellationDataTransferD3D11(ID3D11DeviceContext *context, ID3D11Device *device) + : TessellationDataTransfer(), context_(context), device_(device), buf(), view(), desc() { + desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; } ~TessellationDataTransferD3D11() { - for (int i = 0; i < 3; i++) { - if (data_tex[i]) { - data_tex[i]->Release(); - view[i]->Release(); - } + if (buf) { + buf->Release(); + view->Release(); } } + + void PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) override; void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; }; }; diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 227c51223c..5aafb19d18 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -263,9 +263,12 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage // Hardware tessellation if (doSpline || doBezier) { if (lang == HLSL_D3D11 || lang == HLSL_D3D11_LEVEL9) { - WRITE(p, "Texture1D u_tess_pos_tex : register(t0);\n"); - WRITE(p, "Texture1D u_tess_tex_tex : register(t1);\n"); - WRITE(p, "Texture1D u_tess_col_tex : register(t2);\n"); + WRITE(p, "struct TessData {\n"); + WRITE(p, " float3 pos; float pad1;\n"); + WRITE(p, " float2 tex; float2 pad2;\n"); + WRITE(p, " float4 col;\n"); + WRITE(p, "};"); + WRITE(p, "StructuredBuffer tess_data : register(t0);\n"); } const char *init[3] = { "0.0, 0.0", "0.0, 0.0, 0.0", "0.0, 0.0, 0.0, 0.0" }; @@ -401,17 +404,15 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage WRITE(p, " float3 _pos[16];\n"); WRITE(p, " float2 _tex[16];\n"); WRITE(p, " float4 _col[16];\n"); - WRITE(p, " int idx;\n"); - WRITE(p, " int2 index;\n"); + WRITE(p, " int index;\n"); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - WRITE(p, " idx = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : ""); - WRITE(p, " index = int2(idx, 0);\n"); - WRITE(p, " _pos[%i] = u_tess_pos_tex.Load(index).xyz;\n", i * 4 + j); + WRITE(p, " index = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : ""); + WRITE(p, " _pos[%i] = tess_data[index].pos;\n", i * 4 + j); if (doTexture && hasTexcoord && hasTexcoordTess) - WRITE(p, " _tex[%i] = u_tess_tex_tex.Load(index).xy;\n", i * 4 + j); + WRITE(p, " _tex[%i] = tess_data[index].tex;\n", i * 4 + j); if (hasColor && hasColorTess) - WRITE(p, " _col[%i] = u_tess_col_tex.Load(index).rgba;\n", i * 4 + j); + WRITE(p, " _col[%i] = tess_data[index].col;\n", i * 4 + j); } } WRITE(p, " float2 weights[4];\n"); @@ -441,7 +442,7 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage if (hasColorTess) WRITE(p, " float4 col = tess_sample(_col, weights);\n"); else - WRITE(p, " float4 col = u_tess_col_tex.Load(int2(0, 0)).rgba;\n"); + WRITE(p, " float4 col = tess_data[0].col;\n"); } if (hasNormal) { // Curved surface is probably always need to compute normal(not sampling from control points) From 55b9daa736dd41904e3cd1b35e848f6290c24dc8 Mon Sep 17 00:00:00 2001 From: xebra Date: Wed, 27 Jun 2018 01:25:08 +0900 Subject: [PATCH 286/301] GE Debugger:Fix indices memory allocation of bezier. --- Windows/GEDebugger/VertexPreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index c17c6d3638..42e006efd0 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -199,7 +199,7 @@ static void ExpandBezier(int &count, int op, const std::vector &si } generatedVerts.resize((tess_u + 1) * (tess_v + 1) * total_patches); - generatedInds.resize(tess_u * tess_v * 6); + generatedInds.resize(tess_u * tess_v * 6 * total_patches); count = 0; u8 *dest = (u8 *)&generatedVerts[0]; From 1517a93d0821b8334abd8be53d67b3c5b68ab264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 26 Jun 2018 19:49:49 +0200 Subject: [PATCH 287/301] Restore the vertexCount to 32-bit, needed in some tesselation situations --- GPU/Common/DrawEngineCommon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 7e7f09443b..03e9e50cf3 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -137,9 +137,9 @@ protected: struct DeferredDrawCall { void *verts; void *inds; + u32 vertexCount; u8 indexType; s8 prim; - u16 vertexCount; u16 indexLowerBound; u16 indexUpperBound; UVScale uvScale; From e4fea3be6a14cf94ec90f93321a738583d054ab4 Mon Sep 17 00:00:00 2001 From: hissingshark Date: Fri, 15 Jun 2018 21:29:56 +0100 Subject: [PATCH 288/301] Added Vulkan disable and forced CPU detection options --- CMakeLists.txt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ae5fa9024..b009b7d76f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,11 @@ if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") message("Clang enabled") endif() +if(FORCED_CPU) + message("Detected CPU (${CMAKE_SYSTEM_PROCESSOR}) overridden as: ${FORCED_CPU}") + set(CMAKE_SYSTEM_PROCESSOR ${FORCED_CPU}) +endif() + # Detect CPU from CMAKE configuration. Toolchains should set this up if(CMAKE_SYSTEM_PROCESSOR) if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm") @@ -75,10 +80,15 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Android") endif() # We only support Vulkan on Unix, Android and Windows. -if(ANDROID OR WIN32 OR (UNIX AND NOT APPLE)) - set(VULKAN ON) +if(DISABLE_VULKAN) + set(VULKAN OFF) + message("Vulkan disabled") else() - add_definitions(-DNO_VULKAN) + if(ANDROID OR WIN32 OR (UNIX AND NOT APPLE)) + set(VULKAN ON) + else() + add_definitions(-DNO_VULKAN) + endif() endif() if(NOT DEFINED HEADLESS) @@ -122,6 +132,7 @@ option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_F option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP}) option(USE_WAYLAND_WSI "Set to ON to require Wayland support for Vulkan" ${USE_WAYLAND_WSI}) option(USE_ADDRESS_SANITIZER "Use Clang memory sanitizer" ${USE_ADDRESS_SANITIZER}) +option(DISABLE_VULKAN "Set to ON to if building for Vulkan is unsupported" ${DISABLE_VULKAN}) if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN) add_definitions(-DVK_USE_PLATFORM_XLIB_KHR) From d1f116356fcce17c48e5637fe723e5922ed3ecb5 Mon Sep 17 00:00:00 2001 From: hissingshark Date: Tue, 19 Jun 2018 21:44:55 +0100 Subject: [PATCH 289/301] Changed cmake option to exclude X11 rather than Vulkan itself. --- CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b009b7d76f..80be1d28d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,15 +80,10 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Android") endif() # We only support Vulkan on Unix, Android and Windows. -if(DISABLE_VULKAN) - set(VULKAN OFF) - message("Vulkan disabled") +if(ANDROID OR WIN32 OR (UNIX AND NOT APPLE)) + set(VULKAN ON) else() - if(ANDROID OR WIN32 OR (UNIX AND NOT APPLE)) - set(VULKAN ON) - else() - add_definitions(-DNO_VULKAN) - endif() + add_definitions(-DNO_VULKAN) endif() if(NOT DEFINED HEADLESS) @@ -119,6 +114,7 @@ option(USING_EGL "Set to ON if target environment uses EGL" ${USING_EGL}) option(USING_FBDEV "Set to ON if target environment uses fbdev (eg. Pandora)" ${USING_FBDEV}) option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2}) option(RASPBIAN "Set to ON to workaround threading issues when building for ARMV7 on Raspbian" ${RASPBIAN}) +option(USING_X11_VULKAN "Set to ON if target environment uses X11 for Vulkan" ${USING_X11_VULKAN}) # :: Frontends option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI}) option(MOBILE_DEVICE "Set to ON when targeting a mobile device" ${MOBILE_DEVICE}) @@ -132,10 +128,14 @@ option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_F option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP}) option(USE_WAYLAND_WSI "Set to ON to require Wayland support for Vulkan" ${USE_WAYLAND_WSI}) option(USE_ADDRESS_SANITIZER "Use Clang memory sanitizer" ${USE_ADDRESS_SANITIZER}) -option(DISABLE_VULKAN "Set to ON to if building for Vulkan is unsupported" ${DISABLE_VULKAN}) if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN) - add_definitions(-DVK_USE_PLATFORM_XLIB_KHR) + if(USING_X11_VULKAN) + message("Using X11 for Vulkan") + add_definitions(-DVK_USE_PLATFORM_XLIB_KHR) + else() + message("NOT using X11 for Vulkan") + endif() # add_definitions(-DVK_USE_PLATFORM_XCB_KHR) find_package(Wayland) if (USE_WAYLAND_WSI AND NOT WAYLAND_FOUND) From 90fbbaba1ab7d6c78e2180371a9834d7789c8a3c Mon Sep 17 00:00:00 2001 From: hissingshark Date: Wed, 27 Jun 2018 12:48:59 +0100 Subject: [PATCH 290/301] Made X11-Vulkan default. Provided explanatory toolchain file as overriding is more complex. --- CMakeLists.txt | 2 +- cmake/Toolchains/vero4k.armv8.cmake | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 cmake/Toolchains/vero4k.armv8.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 80be1d28d1..bffaa11320 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,7 @@ option(USING_EGL "Set to ON if target environment uses EGL" ${USING_EGL}) option(USING_FBDEV "Set to ON if target environment uses fbdev (eg. Pandora)" ${USING_FBDEV}) option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2}) option(RASPBIAN "Set to ON to workaround threading issues when building for ARMV7 on Raspbian" ${RASPBIAN}) -option(USING_X11_VULKAN "Set to ON if target environment uses X11 for Vulkan" ${USING_X11_VULKAN}) +option(USING_X11_VULKAN "Set to OFF if target environment doesn't use X11 for Vulkan" ON) # :: Frontends option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI}) option(MOBILE_DEVICE "Set to ON when targeting a mobile device" ${MOBILE_DEVICE}) diff --git a/cmake/Toolchains/vero4k.armv8.cmake b/cmake/Toolchains/vero4k.armv8.cmake new file mode 100644 index 0000000000..28720127b3 --- /dev/null +++ b/cmake/Toolchains/vero4k.armv8.cmake @@ -0,0 +1,22 @@ +include_directories(SYSTEM + /opt/vero3/include + /opt/vero3/include/EGL + /opt/vero3/include/GLES2 +) + +set(ARCH_FLAGS "-march=armv8-a+crc -mtune=cortex-a53 -mfloat-abi=hard -funsafe-math-optimizations") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) + +set(CMAKE_EXE_LINKER_FLAGS "-L/opt/vero3/lib" CACHE STRING "" FORCE) + +set(OPENGL_LIBRARIES /opt/vero3/lib/libGLESv2.so) +set(EGL_LIBRARIES /opt/vero3/lib/libEGL.so) + +set(USING_GLES2 ON) +set(USING_EGL ON) +set(USING_FBDEV ON) +set(ARMV7 ON) +set(FORCED_CPU armv7) +set(USING_X11_VULKAN OFF CACHE BOOL "" FORCE) From 9bac60397646bfd49d955ded47db4a835b3ccf94 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 28 Jun 2018 19:30:20 -0700 Subject: [PATCH 291/301] GPU: Use HW transform for flat spline/bezier. We can control the provoking vertex here, but it's less likely to matter. --- GPU/D3D11/DrawEngineD3D11.cpp | 9 +++++---- GPU/Directx9/ShaderManagerDX9.cpp | 3 ++- GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index be7ffd9e83..fb7b001b7b 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -325,14 +325,15 @@ void DrawEngineD3D11::DoFlush() { gpuStats.numFlushes++; gpuStats.numTrackedVertexArrays = (int)vai_.size(); - // This is not done on every drawcall, we should collect vertex data + // This is not done on every drawcall, we collect vertex data // until critical state changes. That's when we draw (flush). GEPrimitiveType prim = prevPrim_; ApplyDrawState(prim); // Always use software for flat shading to fix the provoking index. - bool useHWTransform = CanUseHardwareTransform(prim) && gstate.getShadeMode() != GE_SHADE_FLAT; + bool tess = gstate_c.bezier || gstate_c.spline; + bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); if (useHWTransform) { ID3D11Buffer *vb_ = nullptr; @@ -539,7 +540,7 @@ rotateVBO: memcpy(iptr, decIndex, iSize); pushInds_->EndPush(context_); context_->IASetIndexBuffer(pushInds_->Buf(), DXGI_FORMAT_R16_UINT, iOffset); - if (gstate_c.bezier || gstate_c.spline) + if (tess) context_->DrawIndexedInstanced(vertexCount, numPatches, 0, 0, 0); else context_->DrawIndexed(vertexCount, 0, 0); @@ -551,7 +552,7 @@ rotateVBO: context_->IASetVertexBuffers(0, 1, &vb_, &stride, &offset); if (useElements) { context_->IASetIndexBuffer(ib_, DXGI_FORMAT_R16_UINT, 0); - if (gstate_c.bezier || gstate_c.spline) + if (tess) context_->DrawIndexedInstanced(vertexCount, numPatches, 0, 0, 0); else context_->DrawIndexed(vertexCount, 0, 0); diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index f910f6cc8f..2e01495aa2 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -538,7 +538,8 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { // Always use software for flat shading to fix the provoking index. - bool useHWTransform = CanUseHardwareTransform(prim) && gstate.getShadeMode() != GE_SHADE_FLAT; + bool tess = gstate_c.bezier || gstate_c.spline; + bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 405c10fc54..0da871ba3e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -578,7 +578,7 @@ void DrawEngineVulkan::DoFlush() { GEPrimitiveType prim = prevPrim_; // Always use software for flat shading to fix the provoking index. - bool useHWTransform = CanUseHardwareTransform(prim) && gstate.getShadeMode() != GE_SHADE_FLAT; + bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); VulkanVertexShader *vshader = nullptr; VulkanFragmentShader *fshader = nullptr; From 2a13a66e18ac28de223c1b8de077aad2de022758 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 28 Jun 2018 19:47:33 -0700 Subject: [PATCH 292/301] GPU: Use hw tess only for hw transform. This may be needed if we start using software for points/lines. --- GPU/Common/SplineCommon.cpp | 22 +++++++++++++--------- GPU/Common/SplineCommon.h | 1 + GPU/GPUCommon.cpp | 9 +++++---- GPU/ge_constants.h | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 948c31b1f2..a4ce9f844d 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -24,6 +24,7 @@ #include "Common/MemoryUtil.h" #include "Core/Config.h" +#include "GPU/Common/GPUStateUtils.h" #include "GPU/Common/SplineCommon.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/ge_constants.h" @@ -223,6 +224,13 @@ static void spline_knot(int n, int type, float *knot) { } } +bool CanUseHardwareTessellation(GEPatchPrimType prim) { + if (g_Config.bHardwareTessellation && !g_Config.bSoftwareRendering) { + return CanUseHardwareTransform(PatchPrimToPrim(prim)); + } + return false; +} + // Prepare mesh of one patch for "Instanced Tessellation". static void TessellateSplinePatchHardware(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch) { SimpleVertex *&vertices = (SimpleVertex*&)dest; @@ -841,9 +849,6 @@ void TessellateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int } } -// This maps GEPatchPrimType to GEPrimitiveType. -const GEPrimitiveType primType[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS }; - void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, int type_u, int type_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead) { PROFILE_THIS_SCOPE("spline"); DispatchFlush(); @@ -900,8 +905,7 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi patch.primType = prim_type; patch.patchFacing = patchFacing; - if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { - + if (CanUseHardwareTessellation(prim_type)) { float *pos = (float*)(decoded + 65536 * 18); // Size 4 float float *tex = pos + count_u * count_v * 4; // Size 4 float float *col = tex + count_u * count_v * 4; // Size 4 float @@ -952,7 +956,7 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi uint32_t vertTypeID = GetVertTypeID(vertTypeWithIndex16, gstate.getUVGenMode()); int generatedBytesRead; - DispatchSubmitPrim(splineBuffer, quadIndices_, primType[prim_type], count, vertTypeID, &generatedBytesRead); + DispatchSubmitPrim(splineBuffer, quadIndices_, PatchPrimToPrim(prim_type), count, vertTypeID, &generatedBytesRead); DispatchFlush(); @@ -1006,7 +1010,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi int num_patches_u = (count_u - 1) / 3; int num_patches_v = (count_v - 1) / 3; BezierPatch *patches = nullptr; - if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + if (CanUseHardwareTessellation(prim_type)) { int posStride, texStride, colStride; tessDataTransfer->PrepareBuffers(pos, tex, col, posStride, texStride, colStride, count_u * count_v, hasColor, hasTexCoords); float *p = pos; @@ -1063,7 +1067,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi } u16 *inds = quadIndices_; - if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + if (CanUseHardwareTessellation(prim_type)) { tessDataTransfer->SendDataToShader(pos, tex, col, count_u * count_v, hasColor, hasTexCoords); TessellateBezierPatchHardware(dest, inds, count, tess_u, tess_v, prim_type); numPatches = num_patches_u * num_patches_v; @@ -1095,7 +1099,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi uint32_t vertTypeID = GetVertTypeID(vertTypeWithIndex16, gstate.getUVGenMode()); int generatedBytesRead; - DispatchSubmitPrim(splineBuffer, quadIndices_, primType[prim_type], count, vertTypeID, &generatedBytesRead); + DispatchSubmitPrim(splineBuffer, quadIndices_, PatchPrimToPrim(prim_type), count, vertTypeID, &generatedBytesRead); DispatchFlush(); diff --git a/GPU/Common/SplineCommon.h b/GPU/Common/SplineCommon.h index 7291f5bc9f..52bf75b69b 100644 --- a/GPU/Common/SplineCommon.h +++ b/GPU/Common/SplineCommon.h @@ -66,5 +66,6 @@ enum SplineQuality { HIGH_QUALITY = 2, }; +bool CanUseHardwareTessellation(GEPatchPrimType prim); void TessellateSplinePatch(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch, u32 origVertType, int maxVertices); void TessellateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 5eb2cc595b..9a6a471ae8 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -24,9 +24,10 @@ #include "Core/HLE/sceGe.h" #include "Core/Debugger/Breakpoints.h" #include "Core/MemMapHelpers.h" -#include "GPU/Common/FramebufferCommon.h" -#include "GPU/Common/TextureCacheCommon.h" #include "GPU/Common/DrawEngineCommon.h" +#include "GPU/Common/FramebufferCommon.h" +#include "GPU/Common/SplineCommon.h" +#include "GPU/Common/TextureCacheCommon.h" #include "GPU/Debugger/Record.h" const CommonCommandTableEntry commonCommandTable[] = { @@ -1729,7 +1730,7 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { bool computeNormals = gstate.isLightingEnabled(); bool patchFacing = gstate.patchfacing & 1; - if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + if (CanUseHardwareTessellation(patchPrim)) { gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); gstate_c.bezier = true; if (gstate_c.spline_count_u != bz_ucount) { @@ -1793,7 +1794,7 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { bool patchFacing = gstate.patchfacing & 1; u32 vertType = gstate.vertType; - if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + if (CanUseHardwareTessellation(patchPrim)) { gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); gstate_c.spline = true; bool countsChanged = gstate_c.spline_count_u != sp_ucount || gstate_c.spline_count_v != sp_vcount; diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 1bba05640d..c0b1bc067e 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -563,7 +563,7 @@ inline GEPrimitiveType PatchPrimToPrim(GEPatchPrimType type) { case GE_PATCHPRIM_TRIANGLES: return GE_PRIM_TRIANGLES; case GE_PATCHPRIM_LINES: return GE_PRIM_LINES; case GE_PATCHPRIM_POINTS: return GE_PRIM_POINTS; - case GE_PATCHPRIM_UNKNOWN: default: return GE_PRIM_KEEP_PREVIOUS; // just something + case GE_PATCHPRIM_UNKNOWN: default: return GE_PRIM_POINTS; // Treated as points. } } From b13c5fbe5da8122eb8887c89b3f0844529baec15 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Jun 2018 09:16:25 -0700 Subject: [PATCH 293/301] GLES: Read compile/link errors consistently. Sometimes blank ones are getting reported, so let's be more careful. --- ext/native/thin3d/GLQueueRunner.cpp | 75 +++++++++++++++-------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 49406aea8b..5567e6332f 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -72,6 +72,24 @@ void GLQueueRunner::DestroyDeviceObjects() { CHECK_GL_ERROR_IF_DEBUG(); } +template +static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) { + GLint bufLength = 0; + getiv(name, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength <= 0) + bufLength = 2048; + + std::string infoLog; + infoLog.resize(bufLength); + GLsizei len = 0; + getLog(name, (GLsizei)infoLog.size(), &len, &infoLog[0]); + if (len <= 0) + return "(unknown reason)"; + + infoLog.resize(len); + return infoLog; +} + void GLQueueRunner::RunInitSteps(const std::vector &steps) { CHECK_GL_ERROR_IF_DEBUG(); glActiveTexture(GL_TEXTURE0); @@ -143,36 +161,28 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { GLint linkStatus = GL_FALSE; glGetProgramiv(program->program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { - GLint bufLength = 0; - glGetProgramiv(program->program, GL_INFO_LOG_LENGTH, &bufLength); - if (bufLength) { - char *buf = new char[bufLength]; - glGetProgramInfoLog(program->program, bufLength, nullptr, buf); + std::string infoLog = GetInfoLog(program->program, glGetProgramiv, glGetProgramInfoLog); - // TODO: Could be other than vs/fs. Also, we're assuming order here... - const char *vsDesc = step.create_program.shaders[0]->desc.c_str(); - const char *fsDesc = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->desc.c_str() : nullptr; - const char *vsCode = step.create_program.shaders[0]->code.c_str(); - const char *fsCode = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->code.c_str() : nullptr; - Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", buf, fsDesc, fsCode, vsDesc, vsCode); + // TODO: Could be other than vs/fs. Also, we're assuming order here... + const char *vsDesc = step.create_program.shaders[0]->desc.c_str(); + const char *fsDesc = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->desc.c_str() : nullptr; + const char *vsCode = step.create_program.shaders[0]->code.c_str(); + const char *fsCode = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->code.c_str() : nullptr; + Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", infoLog.c_str(), fsDesc, fsCode, vsDesc, vsCode); - ELOG("Could not link program:\n %s", buf); - ERROR_LOG(G3D, "VS desc:\n%s", vsDesc); - ERROR_LOG(G3D, "FS desc:\n%s", fsDesc); - ERROR_LOG(G3D, "VS:\n%s\n", vsCode); - ERROR_LOG(G3D, "FS:\n%s\n", fsCode); + ELOG("Could not link program:\n %s", infoLog.c_str()); + ERROR_LOG(G3D, "VS desc:\n%s", vsDesc); + ERROR_LOG(G3D, "FS desc:\n%s", fsDesc); + ERROR_LOG(G3D, "VS:\n%s\n", vsCode); + ERROR_LOG(G3D, "FS:\n%s\n", fsCode); #ifdef _WIN32 - OutputDebugStringUTF8(buf); - if (vsCode) - OutputDebugStringUTF8(LineNumberString(vsCode).c_str()); - if (fsCode) - OutputDebugStringUTF8(LineNumberString(fsCode).c_str()); + OutputDebugStringUTF8(infoLog.c_str()); + if (vsCode) + OutputDebugStringUTF8(LineNumberString(vsCode).c_str()); + if (fsCode) + OutputDebugStringUTF8(LineNumberString(fsCode).c_str()); #endif - delete[] buf; - } else { - ELOG("Could not link program with %d shaders for unknown reason:", step.create_program.num_shaders); - } CHECK_GL_ERROR_IF_DEBUG(); break; } @@ -211,23 +221,18 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { GLint success = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { -#define MAX_INFO_LOG_SIZE 2048 - GLchar infoLog[MAX_INFO_LOG_SIZE]; - GLsizei len = 0; - glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog); - infoLog[len] = '\0'; + std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog); #ifdef __ANDROID__ - ELOG("Error in shader compilation! %s\n", infoLog); + ELOG("Error in shader compilation! %s\n", infoLog.c_str()); ELOG("Shader source:\n%s\n", (const char *)code); #endif ERROR_LOG(G3D, "Error in shader compilation for: %s", step.create_shader.shader->desc.c_str()); - ERROR_LOG(G3D, "Info log: %s", infoLog); + ERROR_LOG(G3D, "Info log: %s", infoLog.c_str()); ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code); - Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog, step.create_shader.shader->desc.c_str(), (const char *)code); + Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog.c_str(), step.create_shader.shader->desc.c_str(), (const char *)code); #ifdef SHADERLOG - OutputDebugStringUTF8(infoLog); + OutputDebugStringUTF8(infoLog.c_str()); #endif - step.create_shader.shader->valid = false; step.create_shader.shader->failed = true; step.create_shader.shader->error = infoLog; } From 0b245699bb73420209467f57dbdf79d8e3ec2c28 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Jun 2018 09:41:12 -0700 Subject: [PATCH 294/301] GLES: Specify unsigned shift for Adreno bug. Adreno 320 devices, and at least 1 Intel device, are complaining that right shift can't be used within uint/int. Possibly related to: https://github.com/KhronosGroup/glslang/issues/1296 --- GPU/Common/DepalettizeShaderCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 19a14c2dfc..e691f25459 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -114,7 +114,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang texturePixels = 512; if (shift) { - WRITE(p, " index = (int(uint(index) >> %i) & 0x%02x)", shift, mask); + WRITE(p, " index = (int(uint(index) >> uint(%i)) & 0x%02x)", shift, mask); } else { WRITE(p, " index = (index & 0x%02x)", mask); } From 716e6ad3a21cce29699b4d9abd2dc491227c8f21 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Jun 2018 10:10:42 -0700 Subject: [PATCH 295/301] GLES: Include shader compile status in log/report. --- ext/native/thin3d/GLQueueRunner.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 5567e6332f..1ae55e39f6 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -164,15 +164,17 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { std::string infoLog = GetInfoLog(program->program, glGetProgramiv, glGetProgramInfoLog); // TODO: Could be other than vs/fs. Also, we're assuming order here... - const char *vsDesc = step.create_program.shaders[0]->desc.c_str(); - const char *fsDesc = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->desc.c_str() : nullptr; - const char *vsCode = step.create_program.shaders[0]->code.c_str(); - const char *fsCode = step.create_program.num_shaders > 1 ? step.create_program.shaders[1]->code.c_str() : nullptr; - Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", infoLog.c_str(), fsDesc, fsCode, vsDesc, vsCode); + GLRShader *vs = step.create_program.shaders[0]; + GLRShader *fs = step.create_program.num_shaders > 1 ? step.create_program.shaders[1] : nullptr; + std::string vsDesc = vs->desc + (vs->failed ? " (failed)" : ""); + std::string fsDesc = fs ? (fs->desc + (fs->failed ? " (failed)" : "")) : "(none)"; + const char *vsCode = vs->code.c_str(); + const char *fsCode = fs ? fs->code.c_str() : "(none)"; + Reporting::ReportMessage("Error in shader program link: info: %s\nfs: %s\n%s\nvs: %s\n%s", infoLog.c_str(), fsDesc.c_str(), fsCode, vsDesc.c_str(), vsCode); ELOG("Could not link program:\n %s", infoLog.c_str()); - ERROR_LOG(G3D, "VS desc:\n%s", vsDesc); - ERROR_LOG(G3D, "FS desc:\n%s", fsDesc); + ERROR_LOG(G3D, "VS desc:\n%s", vsDesc.c_str()); + ERROR_LOG(G3D, "FS desc:\n%s", fsDesc.c_str()); ERROR_LOG(G3D, "VS:\n%s\n", vsCode); ERROR_LOG(G3D, "FS:\n%s\n", fsCode); From 1976be48abc16938a4a3424491d336d9b8e04f4c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 30 Jun 2018 12:17:52 -0700 Subject: [PATCH 296/301] Savedata: Use file hash to validate hash mode. This makes older PPSSPP save data also work, and also logs when save data is detected as corrupt. --- Core/Dialog/SavedataParam.cpp | 146 ++++++++++++++++++++-------------- Core/Dialog/SavedataParam.h | 14 +++- 2 files changed, 97 insertions(+), 63 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 7fd7d8a489..3182f3b3df 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -435,39 +435,31 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD sfoFile.SetValue("SAVEDATA_DIRECTORY", GetSaveDir(param, saveDirName), 64); // For each file, 13 bytes for filename, 16 bytes for file hash (0 in PPSSPP), 3 byte for padding - if (secureMode) - { - const int FILE_LIST_ITEM_SIZE = 13 + 16 + 3; + if (secureMode) { const int FILE_LIST_COUNT_MAX = 99; - const u32 FILE_LIST_TOTAL_SIZE = FILE_LIST_ITEM_SIZE * FILE_LIST_COUNT_MAX; + const u32 FILE_LIST_TOTAL_SIZE = sizeof(SaveSFOFileListEntry) * FILE_LIST_COUNT_MAX; u32 tmpDataSize = 0; - u8 *tmpDataOrig = sfoFile.GetValueData("SAVEDATA_FILE_LIST", &tmpDataSize); - u8 *tmpData = new u8[FILE_LIST_TOTAL_SIZE]; + SaveSFOFileListEntry *tmpDataOrig = (SaveSFOFileListEntry *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &tmpDataSize); + SaveSFOFileListEntry *updatedList = new SaveSFOFileListEntry[FILE_LIST_COUNT_MAX]; + if (tmpDataSize != 0) + memcpy(updatedList, tmpDataOrig, std::min(tmpDataSize, FILE_LIST_TOTAL_SIZE)); + if (tmpDataSize < FILE_LIST_TOTAL_SIZE) + memset(updatedList + tmpDataSize, 0, FILE_LIST_TOTAL_SIZE - tmpDataSize); - if (tmpDataOrig != NULL) - memcpy(tmpData, tmpDataOrig, tmpDataSize > FILE_LIST_TOTAL_SIZE ? FILE_LIST_TOTAL_SIZE : tmpDataSize); - else - memset(tmpData, 0, FILE_LIST_TOTAL_SIZE); + if (param->dataBuf.IsValid()) { + const std::string saveFilename = GetFileName(param); + for (auto entry = updatedList; entry < updatedList + FILE_LIST_COUNT_MAX; ++entry) { + if (entry->filename[0] != '\0') { + if (strncmp(entry->filename, saveFilename.c_str(), sizeof(entry->filename)) != 0) + continue; + } - if (param->dataBuf.IsValid()) - { - char *fName = (char*)tmpData; - for(int i = 0; i < FILE_LIST_COUNT_MAX; i++) - { - if(fName[0] == 0) - break; // End of list - if(strncmp(fName,GetFileName(param).c_str(),20) == 0) - break; - fName += FILE_LIST_ITEM_SIZE; + snprintf(entry->filename, sizeof(entry->filename), "%s", saveFilename.c_str()); + memcpy(entry->hash, cryptedHash, 16); } - - if (fName + 13 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE) - snprintf(fName, 13, "%s",GetFileName(param).c_str()); - if (fName + 13 + 16 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE) - memcpy(fName+13, cryptedHash, 16); } - sfoFile.SetValue("SAVEDATA_FILE_LIST", tmpData, FILE_LIST_TOTAL_SIZE, (int)FILE_LIST_TOTAL_SIZE); - delete[] tmpData; + sfoFile.SetValue("SAVEDATA_FILE_LIST", (u8 *)updatedList, FILE_LIST_TOTAL_SIZE, (int)FILE_LIST_TOTAL_SIZE); + delete[] updatedList; } // Init param with 0. This will be used to detect crypted save or not on loading @@ -599,10 +591,11 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin WARN_LOG_REPORT(SCEUTILITY, "Savedata version requested: %d", param->secureVersion); } u8 *data_ = param->dataBuf; - std::string filePath = dirPath+"/"+GetFileName(param); + std::string filename = GetFileName(param); + std::string filePath = dirPath + "/" + filename; s64 readSize; INFO_LOG(SCEUTILITY,"Loading file with size %u in %s",param->dataBufSize,filePath.c_str()); - u8* saveData = 0; + u8 *saveData = nullptr; int saveSize = -1; if (!ReadPSPFile(filePath, &saveData, saveSize, &readSize)) { ERROR_LOG(SCEUTILITY,"Error reading file %s",filePath.c_str()); @@ -619,7 +612,10 @@ int SavedataParam::LoadSaveData(SceUtilitySavedataParam *param, const std::strin if (isCrypted) { if (DetermineCryptMode(param) > 1 && !HasKey(param)) return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM; - LoadCryptedSave(param, data_, saveData, saveSize, prevCryptMode, saveDone); + + u8 hash[16]; + bool hasExpectedHash = GetExpectedHash(dirPath, filename, hash); + LoadCryptedSave(param, data_, saveData, saveSize, prevCryptMode, hasExpectedHash ? hash : nullptr, saveDone); // TODO: Should return SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN here if !saveDone. } if (!saveDone) { @@ -646,13 +642,14 @@ int SavedataParam::DetermineCryptMode(const SceUtilitySavedataParam *param) cons return decryptMode; } -void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, bool &saveDone) { +void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone) { int align_len = align16(saveSize); u8 *data_base = new u8[align_len]; u8 *cryptKey = new u8[0x10]; memset(cryptKey, 0, 0x10); int decryptMode = DetermineCryptMode(param); + const int detectedMode = decryptMode; bool hasKey = decryptMode > 1; if (hasKey) { memcpy(cryptKey, param->key, 0x10); @@ -688,7 +685,19 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 hasKey = decryptMode > 1; } - if (DecryptSave(decryptMode, data_base, &saveSize, &align_len, (hasKey?cryptKey:0)) == 0) { + int err = DecryptSave(decryptMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, expectedHash); + // Perhaps the file had the wrong mode.... + if (err != 0 && detectedMode != decryptMode) { + hasKey = detectedMode > 1; + err = DecryptSave(detectedMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, expectedHash); + } + // TODO: Should return an error, but let's just try with a bad hash. + if (err != 0 && expectedHash != nullptr) { + WARN_LOG(SCEUTILITY, "Incorrect hash on save data, likely corrupt"); + err = DecryptSave(decryptMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, nullptr); + } + + if (err == 0) { if (param->dataBuf.IsValid()) memcpy(data, data_base, std::min((u32)saveSize, (u32)param->dataBufSize)); saveDone = true; @@ -721,39 +730,54 @@ void SavedataParam::LoadSFO(SceUtilitySavedataParam *param, const std::string& d } } -std::set SavedataParam::getSecureFileNames(std::string dirPath) { - PSPFileInfo sfoFileInfo = pspFileSystem.GetFileInfo(dirPath + "/" + SFO_FILENAME); - std::set secureFileNames; - if (!sfoFileInfo.exists) - return secureFileNames; +std::vector SavedataParam::GetSFOEntries(const std::string &dirPath) { + std::vector result; + const std::string sfoPath = dirPath + "/" + SFO_FILENAME; + if (!pspFileSystem.GetFileInfo(sfoPath).exists) + return result; ParamSFOData sfoFile; std::vector sfoData; - if (pspFileSystem.ReadEntireFile(dirPath + "/" + SFO_FILENAME, sfoData) >= 0) { + if (pspFileSystem.ReadEntireFile(dirPath + "/" + SFO_FILENAME, sfoData) >= 0) sfoFile.ReadSFO(sfoData); + + const int FILE_LIST_COUNT_MAX = 99; + u32 sfoFileListSize = 0; + SaveSFOFileListEntry *sfoFileList = (SaveSFOFileListEntry *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &sfoFileListSize); + const u32 count = std::min((u32)FILE_LIST_COUNT_MAX, sfoFileListSize / (u32)sizeof(SaveSFOFileListEntry)); + + for (u32 i = 0; i < count; ++i) { + if (sfoFileList[i].filename[0] != '\0') + result.push_back(sfoFileList[i]); } - u32 sfoFileListSize = 0; - char *sfoFileList = (char *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &sfoFileListSize); - const int FILE_LIST_ITEM_SIZE = 13 + 16 + 3; - const u32 FILE_LIST_COUNT_MAX = 99; + return result; +} - // Filenames are 13 bytes long at most. Add a NULL so there's no surprises. - char temp[14]; - temp[13] = '\0'; +std::set SavedataParam::GetSecureFileNames(const std::string &dirPath) { + auto entries = GetSFOEntries(dirPath); - for (u32 i = 0; i < FILE_LIST_COUNT_MAX; ++i) { - // Ends at a NULL filename. - if (i * FILE_LIST_ITEM_SIZE >= sfoFileListSize || sfoFileList[i * FILE_LIST_ITEM_SIZE] == '\0') { - break; - } - - strncpy(temp, &sfoFileList[i * FILE_LIST_ITEM_SIZE], 13); + std::set secureFileNames; + for (auto entry : entries) { + char temp[14]; + truncate_cpy(temp, entry.filename); secureFileNames.insert(temp); } return secureFileNames; } +bool SavedataParam::GetExpectedHash(const std::string &dirPath, const std::string &filename, u8 hash[16]) { + auto entries = GetSFOEntries(dirPath); + + for (auto entry : entries) { + if (strncmp(entry.filename, filename.c_str(), sizeof(entry.filename)) == 0) { + memcpy(hash, entry.hash, sizeof(entry.hash)); + return true; + } + } + return false; +} + void SavedataParam::LoadFile(const std::string& dirPath, const std::string& filename, PspUtilitySavedataFileData *fileData) { std::string filePath = dirPath + "/" + filename; s64 readSize = -1; @@ -816,13 +840,7 @@ int SavedataParam::EncryptData(unsigned int mode, return 0; } -int SavedataParam::DecryptSave(unsigned int mode, - unsigned char *data, - int *dataLen, - int *alignedLen, - unsigned char *cryptkey) -{ - +int SavedataParam::DecryptSave(unsigned int mode, unsigned char *data, int *dataLen, int *alignedLen, unsigned char *cryptkey, const u8 *expectedHash) { pspChnnlsvContext1 ctx1; pspChnnlsvContext2 ctx2; @@ -852,6 +870,14 @@ int SavedataParam::DecryptSave(unsigned int mode, if (sceChnnlsv_21BE78B4_(ctx2) < 0) return -7; + if (expectedHash) { + u8 hash[16]; + if (sceSdGetLastIndex_(ctx1, hash, cryptkey) < 0) + return -7; + if (memcmp(hash, expectedHash, sizeof(hash)) != 0) + return -8; + } + /* The decrypted data starts at data + 0x10, so shift it back. */ memmove(data, data + 0x10, *dataLen); return 0; @@ -1154,7 +1180,7 @@ int SavedataParam::GetFilesList(SceUtilitySavedataParam *param) std::set secureFilenames; if (sfoFileInfo.exists) { - secureFilenames = getSecureFileNames(dirPath); + secureFilenames = GetSecureFileNames(dirPath); } else { return SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN; } diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index f4779a5256..0a26f1aeb1 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -293,6 +293,12 @@ struct SaveFileInfo void DoState(PointerWrap &p); }; +struct SaveSFOFileListEntry { + char filename[13]; + u8 hash[16]; + u8 pad[3]; +}; + class SavedataParam { public: @@ -355,18 +361,20 @@ private: void ClearFileInfo(SaveFileInfo &saveInfo, const std::string &saveName); int LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode); - void LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, bool &saveDone); + void LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone); void LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize); void LoadSFO(SceUtilitySavedataParam *param, const std::string& dirPath); void LoadFile(const std::string& dirPath, const std::string& filename, PspUtilitySavedataFileData *fileData); - int DecryptSave(unsigned int mode, unsigned char *data, int *dataLen, int *alignedLen, unsigned char *cryptkey); + int DecryptSave(unsigned int mode, unsigned char *data, int *dataLen, int *alignedLen, unsigned char *cryptkey, const u8 *expectedHash); int EncryptData(unsigned int mode, unsigned char *data, int *dataLen, int *alignedLen, unsigned char *hash, unsigned char *cryptkey); int UpdateHash(u8* sfoData, int sfoSize, int sfoDataParamsOffset, int encryptmode); int BuildHash(unsigned char *output, unsigned char *data, unsigned int len, unsigned int alignedLen, int mode, unsigned char *cryptkey); int DetermineCryptMode(const SceUtilitySavedataParam *param) const; - std::set getSecureFileNames(std::string dirPath); + std::vector GetSFOEntries(const std::string &dirPath); + std::set GetSecureFileNames(const std::string &dirPath); + bool GetExpectedHash(const std::string &dirPath, const std::string &filename, u8 hash[16]); SceUtilitySavedataParam* pspParam; int selectedSave; From 8ae157e27fdf30165f657db238a89d08363305bc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 29 Jun 2018 22:19:23 -0700 Subject: [PATCH 297/301] Debugger: Update to latest armips. --- Core/Debugger/SymbolMap.cpp | 1 + Core/MIPS/MIPSAsm.cpp | 4 ++-- ext/armips | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index b369c014ce..5786898624 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -29,6 +29,7 @@ #endif #include +#include #include "util/text/utf8.h" #include "zlib.h" diff --git a/Core/MIPS/MIPSAsm.cpp b/Core/MIPS/MIPSAsm.cpp index 6265db2390..08965b4fc2 100644 --- a/Core/MIPS/MIPSAsm.cpp +++ b/Core/MIPS/MIPSAsm.cpp @@ -3,6 +3,7 @@ #endif #include #include +#include #include #include "Common/CommonTypes.h" @@ -66,7 +67,6 @@ private: bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address) { - PspAssemblerFile file; StringList errors; wchar_t str[64]; @@ -76,7 +76,7 @@ bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address) args.mode = ArmipsMode::MEMORY; args.content = str + ConvertUTF8ToWString(line); args.silent = true; - args.memoryFile = &file; + args.memoryFile.reset(new PspAssemblerFile()); args.errorsResult = &errors; if (g_symbolMap) { diff --git a/ext/armips b/ext/armips index 3a55cf3600..9efe336728 160000 --- a/ext/armips +++ b/ext/armips @@ -1 +1 @@ -Subproject commit 3a55cf3600d6f328ae0897699ac06818af2905a4 +Subproject commit 9efe3367284d2d1eeb14fc302a2c12c36e3e255e From 1d430ffac9be0859ed96df77b38188979657b925 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 4 Jul 2018 18:00:06 -0700 Subject: [PATCH 298/301] SaveState: Fix stale state threshold. Micro not milli... --- Core/SaveState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index bb39f8304e..df3a089e68 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -243,7 +243,7 @@ namespace SaveState static bool hasLoadedState = false; static const int STALE_STATE_USES = 2; // 4 hours of total gameplay since the virtual PSP started the game. - static const u64 STALE_STATE_TIME = 4 * 3600 * 1000; + static const u64 STALE_STATE_TIME = 4 * 3600 * 1000000ULL; static int saveStateGeneration = 0; static std::string saveStateInitialGitVersion = ""; From d9d9b14e536407de8673359fc744998b14c022ef Mon Sep 17 00:00:00 2001 From: weihuoya Date: Thu, 5 Jul 2018 15:29:40 +0800 Subject: [PATCH 299/301] Don't cut below 2 --- GPU/Common/SplineCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index a4ce9f844d..7af7926dde 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -841,7 +841,7 @@ void TessellateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int _BezierPatchLowQuality(dest, indices, count, tess_u, tess_v, patch, origVertType); break; case MEDIUM_QUALITY: - _BezierPatchHighQuality(dest, indices, count, tess_u / 2, tess_v / 2, patch, origVertType); + _BezierPatchHighQuality(dest, indices, count, std::max(tess_u / 2, 1), std::max(tess_v / 2, 1), patch, origVertType); break; case HIGH_QUALITY: _BezierPatchHighQuality(dest, indices, count, tess_u, tess_v, patch, origVertType); From 402033030b2e411962284079bae3a38e57be21db Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Sat, 7 Jul 2018 15:45:56 +0200 Subject: [PATCH 300/301] Just a quick fix for a typo made in last big rewrite of cheat engine. --- Core/CwCheat.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 71e6070399..5722145ef5 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -870,25 +870,25 @@ void CWCheatEngine::ExecuteOp(const CheatOperation &op, const CheatCode &cheat, break; case CheatOp::IfAddrEqual: - if (!TestIf(op, [](int a, int b) { return a == b; })) { + if (!TestIfAddr(op, [](int a, int b) { return a == b; })) { i += (size_t)op.ifAddrTypes.skip; } break; case CheatOp::IfAddrNotEqual: - if (!TestIf(op, [](int a, int b) { return a != b; })) { + if (!TestIfAddr(op, [](int a, int b) { return a != b; })) { i += (size_t)op.ifAddrTypes.skip; } break; case CheatOp::IfAddrLess: - if (!TestIf(op, [](int a, int b) { return a < b; })) { + if (!TestIfAddr(op, [](int a, int b) { return a < b; })) { i += (size_t)op.ifAddrTypes.skip; } break; case CheatOp::IfAddrGreater: - if (!TestIf(op, [](int a, int b) { return a > b; })) { + if (!TestIfAddr(op, [](int a, int b) { return a > b; })) { i += (size_t)op.ifAddrTypes.skip; } break; From 535def4b74bbc552f3737d10fdc9c4b14544d9b5 Mon Sep 17 00:00:00 2001 From: weihuoya Date: Mon, 9 Jul 2018 10:00:29 +0800 Subject: [PATCH 301/301] max res x2 --- Core/SaveState.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index df3a089e68..0e6371ed5a 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -851,13 +851,15 @@ namespace SaveState break; case SAVESTATE_SAVE_SCREENSHOT: - tempResult = TakeGameScreenshot(op.filename.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY); + { + int maxRes = g_Config.iInternalResolution > 2 ? 2 : -1; + tempResult = TakeGameScreenshot(op.filename.c_str(), ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, maxRes); callbackResult = tempResult ? Status::SUCCESS : Status::FAILURE; if (!tempResult) { ERROR_LOG(SAVESTATE, "Failed to take a screenshot for the savestate! %s", op.filename.c_str()); } break; - + } default: ERROR_LOG(SAVESTATE, "Savestate failure: unknown operation type %d", op.type); callbackResult = Status::FAILURE;