From 2467fddc01adcd0748d23d8a9b2ae6e249f09f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 18 Sep 2019 00:16:08 +0200 Subject: [PATCH 01/33] Vulkan GPU profiler: Measure the CPU time spent on the render thread recording command buffers. --- ext/native/thin3d/VulkanQueueRunner.cpp | 15 ++++++--- ext/native/thin3d/VulkanQueueRunner.h | 10 +++++- ext/native/thin3d/VulkanRenderManager.cpp | 39 ++++++++++++----------- ext/native/thin3d/VulkanRenderManager.h | 6 ++-- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 8be1ca7120..19b9e1c6a3 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -1,4 +1,6 @@ #include + +#include "base/timeutil.h" #include "DataFormat.h" #include "VulkanQueueRunner.h" #include "VulkanRenderManager.h" @@ -375,7 +377,9 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { return pass; } -void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps, VkQueryPool queryPool, std::vector *timestampDescriptions) { +void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps, QueueProfileContext *profile) { + if (profile) + profile->cpuStartTime = real_time_now(); // Optimizes renderpasses, then sequences them. // Planned optimizations: // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes @@ -465,9 +469,9 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st break; } - if (queryPool) { - vkCmdWriteTimestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, queryPool, (uint32_t)timestampDescriptions->size()); - timestampDescriptions->push_back(StepToString(step)); + if (profile) { + vkCmdWriteTimestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile->queryPool, (uint32_t)profile->timestampDescriptions.size()); + profile->timestampDescriptions.push_back(StepToString(step)); } } @@ -476,6 +480,9 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st for (size_t i = 0; i < steps.size(); i++) { delete steps[i]; } + + if (profile) + profile->cpuEndTime = real_time_now(); } void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 3c840856c6..8bb1adfbdf 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -173,6 +173,14 @@ struct TransitionRequest { VkImageLayout targetLayout; }; +struct QueueProfileContext { + VkQueryPool queryPool; + std::vector timestampDescriptions; + std::string profileSummary; + double cpuStartTime; + double cpuEndTime; +}; + struct VKRStep { VKRStep(VKRStepType _type) : stepType(_type) {} ~VKRStep() {} @@ -232,7 +240,7 @@ public: } // RunSteps can modify steps but will leave it in a valid state. - void RunSteps(VkCommandBuffer cmd, std::vector &steps, VkQueryPool queryPool, std::vector *timestampDescriptions); + void RunSteps(VkCommandBuffer cmd, std::vector &steps, QueueProfileContext *profile); void LogSteps(const std::vector &steps); std::string StepToString(const VKRStep &step) const; diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index f9428cc462..f6c4778c62 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -137,7 +137,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; query_ci.queryCount = MAX_TIMESTAMP_QUERIES; query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP; - res = vkCreateQueryPool(vulkan_->GetDevice(), &query_ci, nullptr, &frameData_[i].timestampQueryPool_); + res = vkCreateQueryPool(vulkan_->GetDevice(), &query_ci, nullptr, &frameData_[i].profile.queryPool); } queueRunner_.CreateDeviceObjects(); @@ -227,7 +227,7 @@ void VulkanRenderManager::StopThread() { frameData.pull_condVar.notify_all(); } // Zero the queries so we don't try to pull them later. - frameData.timestampDescriptions.clear(); + frameData.profile.timestampDescriptions.clear(); } thread_.join(); ILOG("Vulkan submission thread joined. Frame=%d", vulkan_->GetCurFrame()); @@ -298,7 +298,7 @@ VulkanRenderManager::~VulkanRenderManager() { vkDestroyCommandPool(device, frameData_[i].cmdPoolInit, nullptr); vkDestroyCommandPool(device, frameData_[i].cmdPoolMain, nullptr); vkDestroyFence(device, frameData_[i].fence, nullptr); - vkDestroyQueryPool(device, frameData_[i].timestampQueryPool_, nullptr); + vkDestroyQueryPool(device, frameData_[i].profile.queryPool, nullptr); } queueRunner_.DestroyDeviceObjects(); } @@ -375,11 +375,11 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { if (frameData.profilingEnabled_) { // Pull the profiling results from last time and produce a summary! - if (!frameData.timestampDescriptions.empty()) { - int numQueries = (int)frameData.timestampDescriptions.size(); + if (!frameData.profile.timestampDescriptions.empty()) { + int numQueries = (int)frameData.profile.timestampDescriptions.size(); VkResult res = vkGetQueryPoolResults( vulkan_->GetDevice(), - frameData.timestampQueryPool_, 0, numQueries, sizeof(uint64_t) * numQueries, &queryResults[0], sizeof(uint64_t), + frameData.profile.queryPool, 0, numQueries, sizeof(uint64_t) * numQueries, &queryResults[0], sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); if (res == VK_SUCCESS) { double timestampConversionFactor = (double)vulkan_->GetPhysicalDeviceProperties().properties.limits.timestampPeriod * (1.0 / 1000000.0); @@ -390,19 +390,20 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { char line[256]; snprintf(line, sizeof(line), "Total GPU time: %0.3f ms\n", ((double)((queryResults[numQueries - 1] - queryResults[0]) & timestampDiffMask) * timestampConversionFactor)); str << line; + snprintf(line, sizeof(line), "Render CPU time: %0.3f ms\n", (frameData.profile.cpuEndTime - frameData.profile.cpuStartTime) * 1000.0); + str << line; for (int i = 0; i < numQueries - 1; i++) { uint64_t diff = (queryResults[i + 1] - queryResults[i]) & timestampDiffMask; double milliseconds = (double)diff * timestampConversionFactor; - snprintf(line, sizeof(line), "%s: %0.3f ms\n", frameData.timestampDescriptions[i + 1].c_str(), milliseconds); + snprintf(line, sizeof(line), "%s: %0.3f ms\n", frameData.profile.timestampDescriptions[i + 1].c_str(), milliseconds); str << line; } - frameData.profileSummary = str.str(); + frameData.profile.profileSummary = str.str(); } else { - frameData.profileSummary = "(error getting GPU profile - not ready?)"; + frameData.profile.profileSummary = "(error getting GPU profile - not ready?)"; } - } - else { - frameData.profileSummary = "(no GPU profile data collected)"; + } else { + frameData.profile.profileSummary = "(no GPU profile data collected)"; } } @@ -415,16 +416,16 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) { insideFrame_ = true; - frameData.timestampDescriptions.clear(); + frameData.profile.timestampDescriptions.clear(); if (frameData_->profilingEnabled_) { // For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool, // unless we want to limit ourselves to only measure the main cmd buffer. // Reserve the first two queries for initCmd. - frameData.timestampDescriptions.push_back("initCmd Begin"); - frameData.timestampDescriptions.push_back("initCmd"); + frameData.profile.timestampDescriptions.push_back("initCmd Begin"); + frameData.profile.timestampDescriptions.push_back("initCmd"); VkCommandBuffer initCmd = GetInitCmd(); - vkCmdResetQueryPool(initCmd, frameData.timestampQueryPool_, 0, MAX_TIMESTAMP_QUERIES); - vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frameData.timestampQueryPool_, 0); + vkCmdResetQueryPool(initCmd, frameData.profile.queryPool, 0, MAX_TIMESTAMP_QUERIES); + vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, frameData.profile.queryPool, 0); } } @@ -960,7 +961,7 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) { if (frameData.hasInitCommands) { if (frameData.profilingEnabled_ && triggerFence) { // Pre-allocated query ID 1. - vkCmdWriteTimestamp(frameData.initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.timestampQueryPool_, 1); + vkCmdWriteTimestamp(frameData.initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, frameData.profile.queryPool, 1); } VkResult res = vkEndCommandBuffer(frameData.initCmd); _assert_msg_(G3D, res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res)); @@ -1061,7 +1062,7 @@ void VulkanRenderManager::Run(int frame) { auto &stepsOnThread = frameData_[frame].steps; VkCommandBuffer cmd = frameData.mainCmd; // queueRunner_.LogSteps(stepsOnThread); - queueRunner_.RunSteps(cmd, stepsOnThread, frameData.profilingEnabled_ ? frameData.timestampQueryPool_ : VK_NULL_HANDLE, &frameData.timestampDescriptions); + queueRunner_.RunSteps(cmd, stepsOnThread, frameData.profilingEnabled_ ? &frameData.profile : nullptr); stepsOnThread.clear(); switch (frameData.type) { diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 56a3fa1cf3..d0b0d74793 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -258,7 +258,7 @@ public: } std::string GetGpuProfileString() const { - return frameData_[vulkan_->GetCurFrame()].profileSummary; + return frameData_[vulkan_->GetCurFrame()].profile.profileSummary; } private: @@ -305,10 +305,8 @@ private: uint32_t curSwapchainImage = -1; // Profiling. - VkQueryPool timestampQueryPool_ = VK_NULL_HANDLE; + QueueProfileContext profile; bool profilingEnabled_; - std::vector timestampDescriptions; - std::string profileSummary; }; FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; From 2eee84b5ce248d42a51442a97cd83d09cc21489c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 09:00:20 -0700 Subject: [PATCH 02/33] Switch: Add basic defines for checks/reporting. --- Core/Reporting.cpp | 2 ++ Windows/PPSSPP.vcxproj | 1 + Windows/PPSSPP.vcxproj.filters | 1 + ppsspp_config.h | 2 ++ 4 files changed, 6 insertions(+) diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 34431ff61f..73636c6a22 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -269,6 +269,8 @@ namespace Reporting return "Mac"; #elif defined(LOONGSON) return "Loongson"; +#elif defined(__SWITCH__) + return "Switch"; #elif defined(__linux__) return "Linux"; #elif defined(__Bitrig__) diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 60f23cc46e..9eed29a578 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -849,6 +849,7 @@ + true diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 9334fab6f3..c640c5642e 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -353,6 +353,7 @@ Windows\System + diff --git a/ppsspp_config.h b/ppsspp_config.h index d2072fbb56..8743e2290e 100644 --- a/ppsspp_config.h +++ b/ppsspp_config.h @@ -110,6 +110,8 @@ #else #error "Unknown Apple platform" #endif +#elif defined(__SWITCH__) + #define PPSSPP_PLATFORM_SWITCH 1 #elif defined(__ANDROID__) #define PPSSPP_PLATFORM_ANDROID 1 #define PPSSPP_PLATFORM_LINUX 1 From a8213e80f75cab88838cc6254c719f15550cf21d Mon Sep 17 00:00:00 2001 From: M4xw Date: Sat, 28 Sep 2019 09:08:32 -0700 Subject: [PATCH 03/33] Switch: Disable IPv6. --- ext/native/file/fd_util.cpp | 10 ++++++++-- ext/native/net/http_client.cpp | 1 - ext/native/net/http_server.cpp | 6 ++++++ ext/native/net/resolve.cpp | 21 +++++++++++++++------ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/ext/native/file/fd_util.cpp b/ext/native/file/fd_util.cpp index 0490c86a3e..38a84f0f22 100644 --- a/ext/native/file/fd_util.cpp +++ b/ext/native/file/fd_util.cpp @@ -1,3 +1,4 @@ +#include "ppsspp_config.h" #include "file/fd_util.h" #include @@ -136,7 +137,9 @@ std::string GetLocalIP(int sock) { union { struct sockaddr sa; struct sockaddr_in ipv4; +#if !PPSSPP_PLATFORM(SWITCH) struct sockaddr_in6 ipv6; +#endif } server_addr; memset(&server_addr, 0, sizeof(server_addr)); socklen_t len = sizeof(server_addr); @@ -144,11 +147,14 @@ std::string GetLocalIP(int sock) { char temp[64]{}; // We clear the port below for WSAAddressToStringA. - void *addr; + void *addr = nullptr; +#if !PPSSPP_PLATFORM(SWITCH) if (server_addr.sa.sa_family == AF_INET6) { server_addr.ipv6.sin6_port = 0; addr = &server_addr.ipv6.sin6_addr; - } else { + } +#endif + if (addr == nullptr) { server_addr.ipv4.sin_port = 0; addr = &server_addr.ipv4.sin_addr; } diff --git a/ext/native/net/http_client.cpp b/ext/native/net/http_client.cpp index 19bff66f48..fff024e0e3 100644 --- a/ext/native/net/http_client.cpp +++ b/ext/native/net/http_client.cpp @@ -86,7 +86,6 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) { int maxfd = 1; 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 && possible->ai_family != AF_INET6) continue; diff --git a/ext/native/net/http_server.cpp b/ext/native/net/http_server.cpp index 099c537782..1c0b5995b3 100644 --- a/ext/native/net/http_server.cpp +++ b/ext/native/net/http_server.cpp @@ -191,6 +191,7 @@ bool Server::Listen4(int port) { } bool Server::Listen6(int port, bool ipv6_only) { +#if !PPSSPP_PLATFORM(SWITCH) listener_ = socket(AF_INET6, SOCK_STREAM, 0); if (listener_ < 0) return false; @@ -232,6 +233,9 @@ bool Server::Listen6(int port, bool ipv6_only) { port_ = port; return true; +#else + return false; +#endif } bool Server::RunSlice(double timeout) { @@ -249,7 +253,9 @@ bool Server::RunSlice(double timeout) { union { struct sockaddr sa; struct sockaddr_in ipv4; +#if !PPSSPP_PLATFORM(SWITCH) struct sockaddr_in6 ipv6; +#endif } client_addr; socklen_t client_addr_size = sizeof(client_addr); int conn_fd = accept(listener_, &client_addr.sa, &client_addr_size); diff --git a/ext/native/net/resolve.cpp b/ext/native/net/resolve.cpp index 60182e4d64..a5bd024c7a 100644 --- a/ext/native/net/resolve.cpp +++ b/ext/native/net/resolve.cpp @@ -43,8 +43,17 @@ void Shutdown() #endif } -bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type) -{ +bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type) { +#if PPSSPP_PLATFORM(SWITCH) + // Force IPv4 lookups. + if (type == DNSType::IPV6) { + *res = nullptr; + return false; + } else if (type == DNSType::ANY) { + type = DNSType::IPV4; + } +#endif + addrinfo hints = {0}; // TODO: Might be uses to lookup other values. hints.ai_socktype = SOCK_STREAM; @@ -61,9 +70,9 @@ bool DNSResolve(const std::string &host, const std::string &service, addrinfo ** else if (type == DNSType::IPV6) hints.ai_family = AF_INET6; - const char *servicep = service.length() == 0 ? NULL : service.c_str(); + const char *servicep = service.length() == 0 ? nullptr : service.c_str(); - *res = NULL; + *res = nullptr; int result = getaddrinfo(host.c_str(), servicep, &hints, res); if (result == EAI_AGAIN) { // Temporary failure. Since this already blocks, let's just try once more. @@ -77,9 +86,9 @@ bool DNSResolve(const std::string &host, const std::string &service, addrinfo ** #else error = gai_strerror(result); #endif - if (*res != NULL) + if (*res != nullptr) freeaddrinfo(*res); - *res = NULL; + *res = nullptr; return false; } From 15a9a0ac8fb18dbd828533e022832859b7b6592f Mon Sep 17 00:00:00 2001 From: M4xw Date: Sat, 28 Sep 2019 09:22:38 -0700 Subject: [PATCH 04/33] Switch: Cleanup some includes. Disable dlopen and tty checks too. --- Common/ConsoleListener.cpp | 2 +- Core/FileSystems/VirtualDiscFileSystem.cpp | 6 +++++- Core/MIPS/MIPSDebugInterface.cpp | 4 ++++ ext/native/base/stringutil.cpp | 5 +++++ ext/native/base/stringutil.h | 2 ++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Common/ConsoleListener.cpp b/Common/ConsoleListener.cpp index b5285e1f25..6b50ae0742 100644 --- a/Common/ConsoleListener.cpp +++ b/Common/ConsoleListener.cpp @@ -68,7 +68,7 @@ ConsoleListener::ConsoleListener() : bHidden(true) bUseColor = false; #elif defined(IOS) bUseColor = false; -#elif PPSSPP_PLATFORM(UWP) +#elif PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(SWITCH) bUseColor = false; #elif defined(_MSC_VER) bUseColor = false; diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index b07946ebf9..e76b5c4dee 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -35,8 +35,10 @@ #include #include #include +#if !PPSSPP_PLATFORM(SWITCH) #include #endif +#endif const std::string INDEX_FILENAME = ".ppsspp-index.lst"; @@ -809,6 +811,7 @@ void VirtualDiscFileSystem::HandlerLogger(void *arg, HandlerHandle handle, LogTy } VirtualDiscFileSystem::Handler::Handler(const char *filename, VirtualDiscFileSystem *const sys) { +#if !PPSSPP_PLATFORM(SWITCH) #ifdef _WIN32 #if PPSSPP_PLATFORM(UWP) #define dlopen(name, ignore) (void *)LoadPackagedLibrary(ConvertUTF8ToWString(name).c_str(), 0) @@ -847,13 +850,14 @@ VirtualDiscFileSystem::Handler::Handler(const char *filename, VirtualDiscFileSys #undef dlsym #undef dlclose #endif +#endif } VirtualDiscFileSystem::Handler::~Handler() { if (library != NULL) { Shutdown(); -#if !PPSSPP_PLATFORM(UWP) +#if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH) #ifdef _WIN32 FreeLibrary((HMODULE)library); #else diff --git a/Core/MIPS/MIPSDebugInterface.cpp b/Core/MIPS/MIPSDebugInterface.cpp index ae1a9a7a6c..e99e86073c 100644 --- a/Core/MIPS/MIPSDebugInterface.cpp +++ b/Core/MIPS/MIPSDebugInterface.cpp @@ -18,6 +18,10 @@ #include #include +#ifndef _MSC_VER +#include +#endif + #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/DebugInterface.h" diff --git a/ext/native/base/stringutil.cpp b/ext/native/base/stringutil.cpp index 0fadb197bc..2861f844d3 100644 --- a/ext/native/base/stringutil.cpp +++ b/ext/native/base/stringutil.cpp @@ -1,8 +1,13 @@ +#include "ppsspp_config.h" #ifdef _WIN32 #include #undef min #undef max #endif +#if PPSSPP_PLATFORM(SWITCH) +#define _GNU_SOURCE +#include +#endif #include #include #include diff --git a/ext/native/base/stringutil.h b/ext/native/base/stringutil.h index 9205c009b6..6a06a739db 100644 --- a/ext/native/base/stringutil.h +++ b/ext/native/base/stringutil.h @@ -13,6 +13,8 @@ #ifdef _MSC_VER #pragma warning (disable:4996) #define strncasecmp _strnicmp +#else +#include #endif // Dumb wrapper around itoa, providing a buffer. Declare this on the stack. From 30fc4c78699d82a8f2aa4e1ae649875941721592 Mon Sep 17 00:00:00 2001 From: M4xw Date: Sat, 28 Sep 2019 09:23:18 -0700 Subject: [PATCH 05/33] Switch: Configure base paths. --- UI/NativeApp.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 03c920bee9..fa4f83042c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -318,6 +318,8 @@ static void PostLoadConfig() { g_Config.currentDirectory = g_Config.externalDirectory; #elif defined(IOS) g_Config.currentDirectory = g_Config.internalDataDirectory; +#elif PPSSPP_PLATFORM(SWITCH) + g_Config.currentDirectory = "/"; #else if (getenv("HOME") != nullptr) g_Config.currentDirectory = getenv("HOME"); @@ -446,12 +448,17 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Packed assets are included in app VFSRegister("", new DirectoryAssetReader(external_dir)); #endif -#if !defined(MOBILE_DEVICE) && !defined(_WIN32) +#if !defined(MOBILE_DEVICE) && !defined(_WIN32) && !PPSSPP_PLATFORM(SWITCH) VFSRegister("", new DirectoryAssetReader((File::GetExeDirectory() + "assets/").c_str())); VFSRegister("", new DirectoryAssetReader((File::GetExeDirectory()).c_str())); VFSRegister("", new DirectoryAssetReader("/usr/share/ppsspp/assets/")); #endif +#if PPSSPP_PLATFORM(SWITCH) + std::string assetPath = savegame_dir + "assets/"; + VFSRegister("", new DirectoryAssetReader(assetPath.c_str())); +#else VFSRegister("", new DirectoryAssetReader("assets/")); +#endif VFSRegister("", new DirectoryAssetReader(savegame_dir)); #if (defined(MOBILE_DEVICE) || !defined(USING_QT_UI)) && !PPSSPP_PLATFORM(UWP) @@ -481,6 +488,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #elif defined(IOS) g_Config.memStickDirectory = user_data_path; g_Config.flash0Directory = std::string(external_dir) + "/flash0/"; +#elif PPSSPP_PLATFORM(SWITCH) + g_Config.memStickDirectory = g_Config.internalDataDirectory + "config/ppsspp/"; + g_Config.flash0Directory = g_Config.internalDataDirectory + "assets/flash0/"; #elif !defined(_WIN32) std::string config; if (getenv("XDG_CONFIG_HOME") != NULL) From bce25bcaedf773dd8bc52fb735c117f719239a83 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 10:45:45 -0700 Subject: [PATCH 06/33] Reporting: Use a single message posting thread. This could cause a short delay when quitting a game, because it now waits for them all to finish posting on exit. Only when reporting enabled. --- Core/Reporting.cpp | 69 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 34431ff61f..da8134b170 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.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 #include #include @@ -70,6 +71,13 @@ namespace Reporting // The latest compatibility result from the server. static std::vector lastCompatResult; + static std::mutex pendingMessageLock; + static std::condition_variable pendingMessageCond; + static std::deque pendingMessages; + static bool pendingMessagesDone = false; + static std::thread messageThread; + static std::thread compatThread; + enum class RequestType { NONE, @@ -93,6 +101,7 @@ namespace Reporting static std::condition_variable crcCond; static std::string crcFilename; static std::map crcResults; + static std::thread crcThread; static int CalculateCRCThread() { setCurrentThreadName("ReportCRC"); @@ -133,8 +142,7 @@ namespace Reporting } crcFilename = gamePath; - std::thread th(CalculateCRCThread); - th.detach(); + crcThread = std::thread(CalculateCRCThread); } u32 RetrieveCRC() { @@ -148,6 +156,8 @@ namespace Reporting it = crcResults.find(gamePath); } + if (crcThread.joinable()) + crcThread.join(); return it->second; } @@ -295,10 +305,20 @@ namespace Reporting logOnceUsed.clear(); everUnsupported = false; currentSupported = IsSupported(); + pendingMessagesDone = false; } void Shutdown() { + pendingMessageLock.lock(); + pendingMessagesDone = true; + pendingMessageCond.notify_one(); + pendingMessageLock.unlock(); + if (compatThread.joinable()) + compatThread.join(); + if (messageThread.joinable()) + messageThread.join(); + // Just so it can be enabled in the menu again. Init(); } @@ -541,6 +561,28 @@ namespace Reporting return -1; } + int ProcessPending() { + setCurrentThreadName("Report"); + + std::unique_lock guard(pendingMessageLock); + while (!pendingMessagesDone) { + while (!pendingMessages.empty() && !pendingMessagesDone) { + int pos = pendingMessages.front(); + pendingMessages.pop_front(); + + guard.unlock(); + Process(pos); + guard.lock(); + } + if (pendingMessagesDone) { + break; + } + pendingMessageCond.wait(guard); + } + + return 0; + } + void ReportMessage(const char *message, ...) { if (!IsEnabled() || CheckSpamLimited()) @@ -563,8 +605,13 @@ namespace Reporting payload.string1 = message; payload.string2 = temp; - std::thread th(Process, pos); - th.detach(); + std::lock_guard guard(pendingMessageLock); + pendingMessages.push_back(pos); + pendingMessageCond.notify_one(); + + if (!messageThread.joinable()) { + messageThread = std::thread(ProcessPending); + } } void ReportMessageFormatted(const char *message, const char *formatted) @@ -580,8 +627,13 @@ namespace Reporting payload.string1 = message; payload.string2 = formatted; - std::thread th(Process, pos); - th.detach(); + std::lock_guard guard(pendingMessageLock); + pendingMessages.push_back(pos); + pendingMessageCond.notify_one(); + + if (!messageThread.joinable()) { + messageThread = std::thread(ProcessPending); + } } void ReportCompatibility(const char *compat, int graphics, int speed, int gameplay, const std::string &screenshotFilename) @@ -600,8 +652,9 @@ namespace Reporting payload.int2 = speed; payload.int3 = gameplay; - std::thread th(Process, pos); - th.detach(); + if (compatThread.joinable()) + compatThread.join(); + compatThread = std::thread(Process, pos); } std::vector CompatibilitySuggestions() { From 2db8c89607e7ce83778ac6030474b71f7d4d903c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 10:48:07 -0700 Subject: [PATCH 07/33] http: Avoid detaching threads, join instead. --- UI/RemoteISOScreen.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 6dd15835f6..7e97b878bd 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -327,7 +327,6 @@ RemoteISOConnectScreen::RemoteISOConnectScreen() : status_(ScanStatus::SCANNING) scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) { thiz->ExecuteScan(); }, this); - scanThread_->detach(); } RemoteISOConnectScreen::~RemoteISOConnectScreen() { @@ -341,6 +340,8 @@ RemoteISOConnectScreen::~RemoteISOConnectScreen() { break; } } + if (scanThread_->joinable()) + scanThread_->join(); delete scanThread_; } @@ -384,11 +385,12 @@ void RemoteISOConnectScreen::update() { status_ = ScanStatus::LOADING; // Let's reuse scanThread_. + if (scanThread_->joinable()) + scanThread_->join(); delete scanThread_; scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) { thiz->ExecuteLoad(); }, this); - scanThread_->detach(); break; case ScanStatus::FAILED: @@ -401,11 +403,12 @@ void RemoteISOConnectScreen::update() { status_ = ScanStatus::SCANNING; nextRetry_ = 0.0; + if (scanThread_->joinable()) + scanThread_->join(); delete scanThread_; scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) { thiz->ExecuteScan(); }, this); - scanThread_->detach(); } break; From c8c0cd51c796b3b852f58c947b775f1aa79a013f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 11:07:57 -0700 Subject: [PATCH 08/33] Core: Avoid detach for game loading thread. --- Core/PSPLoaders.cpp | 24 ++++++++++++++++++------ Core/PSPLoaders.h | 1 + Core/System.cpp | 1 + 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 78e2a7c417..b20bd8abd8 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -57,6 +57,8 @@ #include "Core/HLE/sceKernelModule.h" #include "Core/HLE/sceKernelMemory.h" +static std::thread loadingThread; + static void UseLargeMem(int memsize) { if (memsize != 1) { // Nothing requested. @@ -256,7 +258,11 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { host->SendUIMessage("config_loaded", ""); INFO_LOG(LOADER,"Loading %s...", bootpath.c_str()); - std::thread th([bootpath] { + PSPLoaders_Shutdown(); + // Note: this thread reads the game binary, loads caches, and links HLE while UI spins. + // To do something deterministically when the game starts, disabling this thread won't be enough. + // Instead: Use Core_ListenLifecycle() or watch coreState. + loadingThread = std::thread([bootpath] { setCurrentThreadName("ExecLoader"); PSP_LoadingLock guard; if (coreState != CORE_POWERUP) @@ -273,7 +279,6 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { PSP_CoreParameter().fileToStart = ""; } }); - th.detach(); return true; } @@ -379,7 +384,9 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { } // End of temporary code - std::thread th([finalName] { + PSPLoaders_Shutdown(); + // Note: See Load_PSP_ISO for notes about this thread. + loadingThread = std::thread([finalName] { setCurrentThreadName("ExecLoader"); PSP_LoadingLock guard; if (coreState != CORE_POWERUP) @@ -394,7 +401,6 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { PSP_CoreParameter().fileToStart = ""; } }); - th.detach(); return true; } @@ -402,7 +408,9 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { BlobFileSystem *umd = new BlobFileSystem(&pspFileSystem, fileLoader, "data.ppdmp"); pspFileSystem.Mount("disc0:", umd); - std::thread th([] { + PSPLoaders_Shutdown(); + // Note: See Load_PSP_ISO for notes about this thread. + loadingThread = std::thread([] { setCurrentThreadName("ExecLoader"); PSP_LoadingLock guard; if (coreState != CORE_POWERUP) @@ -417,6 +425,10 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { PSP_CoreParameter().fileToStart = ""; } }); - th.detach(); return true; } + +void PSPLoaders_Shutdown() { + if (loadingThread.joinable()) + loadingThread.join(); +} diff --git a/Core/PSPLoaders.h b/Core/PSPLoaders.h index 34f33ca177..f344008460 100644 --- a/Core/PSPLoaders.h +++ b/Core/PSPLoaders.h @@ -26,3 +26,4 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string); bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string); void InitMemoryForGameISO(FileLoader *fileLoader); void InitMemoryForGamePBP(FileLoader *fileLoader); +void PSPLoaders_Shutdown(); diff --git a/Core/System.cpp b/Core/System.cpp index 67a4e6fd85..d0d0018620 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -269,6 +269,7 @@ PSP_LoadingLock::~PSP_LoadingLock() { void CPU_Shutdown() { // Since we load on a background thread, wait for startup to complete. PSP_LoadingLock lock; + PSPLoaders_Shutdown(); if (g_Config.bAutoSaveSymbolMap) { host->SaveSymbolMap(); From e639dc801fc04624caed3147a16e3eff786922ba Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 11:12:12 -0700 Subject: [PATCH 09/33] http: Ensure webserver shutdown on exit. --- Core/WebServer.cpp | 7 ++++++- Core/WebServer.h | 1 + UI/NativeApp.cpp | 5 +++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 3bec6d622a..8ab773fd1f 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -288,7 +288,6 @@ bool StartWebServer(WebServerFlags flags) { serverStatus = ServerStatus::STARTING; serverFlags = (int)flags; serverThread = std::thread(&ExecuteWebServer); - serverThread.detach(); return true; default: @@ -321,3 +320,9 @@ bool WebServerStopped(WebServerFlags flags) { } return serverStatus == ServerStatus::STOPPED; } + +void ShutdownWebServer() { + StopWebServer(WebServerFlags::ALL); + if (serverThread.joinable()) + serverThread.join(); +} diff --git a/Core/WebServer.h b/Core/WebServer.h index 59b2ea4d2d..d04b888761 100644 --- a/Core/WebServer.h +++ b/Core/WebServer.h @@ -26,5 +26,6 @@ bool StartWebServer(WebServerFlags flags); bool StopWebServer(WebServerFlags flags); bool WebServerStopping(WebServerFlags flags); bool WebServerStopped(WebServerFlags flags); +void ShutdownWebServer(); bool RemoteISOFileSupported(const std::string &filename); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 03c920bee9..30923f6a95 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -869,11 +869,12 @@ void NativeShutdownGraphics() { winAudioBackend = nullptr; #endif + ShutdownWebServer(); + UIBackgroundShutdown(); + delete g_gameInfoCache; g_gameInfoCache = nullptr; - UIBackgroundShutdown(); - delete uiContext; uiContext = nullptr; From cc6115d875e6875995e49bca3710f2d6a003b690 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 11:18:42 -0700 Subject: [PATCH 10/33] Io: Join loading/manager threads more explicitly. --- Core/FileLoaders/CachingFileLoader.cpp | 15 +++++++++------ Core/FileLoaders/CachingFileLoader.h | 4 +++- Core/FileLoaders/RamCachingFileLoader.cpp | 17 ++++++++++------- Core/FileLoaders/RamCachingFileLoader.h | 4 +++- Core/HLE/sceIo.cpp | 6 +++--- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Core/FileLoaders/CachingFileLoader.cpp b/Core/FileLoaders/CachingFileLoader.cpp index 61a7459045..f161574916 100644 --- a/Core/FileLoaders/CachingFileLoader.cpp +++ b/Core/FileLoaders/CachingFileLoader.cpp @@ -109,9 +109,11 @@ void CachingFileLoader::ShutdownCache() { // TODO: Maybe add some hint that deletion is coming soon? // We can't delete while the thread is running, so have to wait. // This should only happen from the menu. - while (aheadThread_) { + while (aheadThreadRunning_) { sleep_ms(1); } + if (aheadThread_.joinable()) + aheadThread_.join(); std::lock_guard guard(blocksMutex_); for (auto block : blocks_) { @@ -252,7 +254,7 @@ bool CachingFileLoader::MakeCacheSpaceFor(size_t blocks, bool readingAhead) { void CachingFileLoader::StartReadAhead(s64 pos) { std::lock_guard guard(blocksMutex_); - if (aheadThread_) { + if (aheadThreadRunning_) { // Already going. return; } @@ -261,8 +263,10 @@ void CachingFileLoader::StartReadAhead(s64 pos) { return; } - aheadThread_ = true; - std::thread th([this, pos] { + aheadThreadRunning_ = true; + if (aheadThread_.joinable()) + aheadThread_.join(); + aheadThread_ = std::thread([this, pos] { setCurrentThreadName("FileLoaderReadAhead"); std::unique_lock guard(blocksMutex_); @@ -278,7 +282,6 @@ void CachingFileLoader::StartReadAhead(s64 pos) { } } - aheadThread_ = false; + aheadThreadRunning_ = false; }); - th.detach(); } diff --git a/Core/FileLoaders/CachingFileLoader.h b/Core/FileLoaders/CachingFileLoader.h index 97fe0aa682..e778265cce 100644 --- a/Core/FileLoaders/CachingFileLoader.h +++ b/Core/FileLoaders/CachingFileLoader.h @@ -19,6 +19,7 @@ #include #include +#include #include "Common/CommonTypes.h" #include "Core/Loaders.h" @@ -75,6 +76,7 @@ private: std::map blocks_; std::recursive_mutex blocksMutex_; - bool aheadThread_ = false; + bool aheadThreadRunning_ = false; + std::thread aheadThread_; std::once_flag preparedFlag_; }; diff --git a/Core/FileLoaders/RamCachingFileLoader.cpp b/Core/FileLoaders/RamCachingFileLoader.cpp index 4b934cebdf..9ea0c2e61f 100644 --- a/Core/FileLoaders/RamCachingFileLoader.cpp +++ b/Core/FileLoaders/RamCachingFileLoader.cpp @@ -105,9 +105,11 @@ void RamCachingFileLoader::ShutdownCache() { // We can't delete while the thread is running, so have to wait. // This should only happen from the menu. - while (aheadThread_) { + while (aheadThreadRunning_) { sleep_ms(1); } + if (aheadThread_.joinable()) + aheadThread_.join(); std::lock_guard guard(blocksMutex_); blocks_.clear(); @@ -118,7 +120,7 @@ void RamCachingFileLoader::ShutdownCache() { } void RamCachingFileLoader::Cancel() { - if (aheadThread_) { + if (aheadThreadRunning_) { std::lock_guard guard(blocksMutex_); aheadCancel_ = true; } @@ -213,14 +215,16 @@ void RamCachingFileLoader::StartReadAhead(s64 pos) { std::lock_guard guard(blocksMutex_); aheadPos_ = pos; - if (aheadThread_) { + if (aheadThreadRunning_) { // Already going. return; } - aheadThread_ = true; + aheadThreadRunning_ = true; aheadCancel_ = false; - std::thread th([this] { + if (aheadThread_.joinable()) + aheadThread_.join(); + aheadThread_ = std::thread([this] { setCurrentThreadName("FileLoaderReadAhead"); while (aheadRemaining_ != 0 && !aheadCancel_) { @@ -243,9 +247,8 @@ void RamCachingFileLoader::StartReadAhead(s64 pos) { } } - aheadThread_ = false; + aheadThreadRunning_ = false; }); - th.detach(); } u32 RamCachingFileLoader::NextAheadBlock() { diff --git a/Core/FileLoaders/RamCachingFileLoader.h b/Core/FileLoaders/RamCachingFileLoader.h index 60ca2afc91..44c375d442 100644 --- a/Core/FileLoaders/RamCachingFileLoader.h +++ b/Core/FileLoaders/RamCachingFileLoader.h @@ -19,6 +19,7 @@ #include #include +#include #include "Common/CommonTypes.h" #include "Core/Loaders.h" @@ -65,6 +66,7 @@ private: std::mutex blocksMutex_; u32 aheadRemaining_; s64 aheadPos_; - bool aheadThread_ = false; + std::thread aheadThread_; + bool aheadThreadRunning_ = false; bool aheadCancel_ = false; }; diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 341b31b8d9..8a04372cd1 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -601,7 +601,6 @@ void __IoInit() { if (ioManagerThreadEnabled) { Core_ListenLifecycle(&__IoWakeManager); ioManagerThread = new std::thread(&__IoManagerThread); - ioManagerThread->detach(); } __KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback); @@ -651,9 +650,10 @@ void __IoShutdown() { ioManagerThreadEnabled = false; ioManager.SyncThread(); ioManager.FinishEventLoop(); - if (ioManagerThread != NULL) { + if (ioManagerThread != nullptr) { + ioManagerThread->join(); delete ioManagerThread; - ioManagerThread = NULL; + ioManagerThread = nullptr; ioManager.Shutdown(); } From 1307273dd7b2d5a4d1ed581aeab32a6328b9dc57 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 11:30:43 -0700 Subject: [PATCH 11/33] UI: Cleanup install threads from UI thread. --- Core/Util/GameManager.cpp | 19 ++++++++++++------- Core/Util/GameManager.h | 5 +++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index f73fe3f316..7a5f08b732 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -44,8 +44,7 @@ GameManager g_GameManager; -GameManager::GameManager() - : installInProgress_(false), installProgress_(0.0f) { +GameManager::GameManager() { } std::string GameManager::GetTempFilename() const { @@ -66,7 +65,7 @@ bool GameManager::IsGameInstalled(std::string name) { } bool GameManager::DownloadAndInstall(std::string storeFileUrl) { - if (curDownload_.get() != 0) { + if (curDownload_.get() != nullptr) { ERROR_LOG(HLE, "Can only process one download at a time"); return false; } @@ -131,6 +130,15 @@ void GameManager::Update() { } curDownload_.reset(); } + + if (installDonePending_) { + if (installThread_.get() != nullptr) { + if (installThread_->joinable()) + installThread_->join(); + installThread_.reset(); + } + installDonePending_ = false; + } } static void countSlashes(const std::string &fileName, int *slashLocation, int *slashCount) { @@ -608,7 +616,6 @@ bool GameManager::InstallGameOnThread(std::string url, std::string fileName, boo return false; } installThread_.reset(new std::thread(std::bind(&GameManager::InstallGame, this, url, fileName, deleteAfter))); - installThread_->detach(); return true; } @@ -628,7 +635,5 @@ bool GameManager::InstallRawISO(const std::string &file, const std::string &orig } void GameManager::InstallDone() { - if (installThread_.get() != 0) { - installThread_.reset(); - } + installDonePending_ = true; } diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 898d24ad4d..81d1368273 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -86,8 +86,9 @@ private: std::string GetISOGameID(FileLoader *loader) const; std::shared_ptr curDownload_; std::shared_ptr installThread_; - bool installInProgress_; - float installProgress_; + bool installInProgress_ = false; + bool installDonePending_ = false; + float installProgress_ = 0.0f; std::string installError_; }; From 987448577f478b7a9ab6a25e214ee9310cc8ec46 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 11:40:10 -0700 Subject: [PATCH 12/33] UI: Join downloads on destruct. --- ext/native/net/http_client.cpp | 9 +++++---- ext/native/net/http_client.h | 13 +++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ext/native/net/http_client.cpp b/ext/native/net/http_client.cpp index 19bff66f48..243cea8a01 100644 --- a/ext/native/net/http_client.cpp +++ b/ext/native/net/http_client.cpp @@ -428,16 +428,16 @@ int Client::ReadResponseEntity(Buffer *readbuf, const std::vector & } Download::Download(const std::string &url, const std::string &outfile) - : progress_(0.0f), url_(url), outfile_(outfile), resultCode_(0), completed_(false), failed_(false), cancelled_(false), hidden_(false) { + : url_(url), outfile_(outfile) { } Download::~Download() { - + if (thread_.joinable()) + thread_.join(); } void Download::Start(std::shared_ptr self) { - std::thread th(std::bind(&Download::Do, this, self)); - th.detach(); + thread_ = std::thread(std::bind(&Download::Do, this, self)); } void Download::SetFailed(int code) { @@ -577,6 +577,7 @@ void Downloader::CancelAll() { for (size_t i = 0; i < downloads_.size(); i++) { downloads_[i]->Cancel(); } + downloads_.clear(); } } // http diff --git a/ext/native/net/http_client.h b/ext/native/net/http_client.h index 3d2d97eb20..b671169585 100644 --- a/ext/native/net/http_client.h +++ b/ext/native/net/http_client.h @@ -142,16 +142,17 @@ private: int PerformGET(const std::string &url); std::string RedirectLocation(const std::string &baseUrl); void SetFailed(int code); - float progress_; + float progress_ = 0.0f; Buffer buffer_; std::vector responseHeaders_; std::string url_; std::string outfile_; - int resultCode_; - bool completed_; - bool failed_; - bool cancelled_; - bool hidden_; + std::thread thread_; + int resultCode_ = 0; + bool completed_ = false; + bool failed_ = false; + bool cancelled_ = false; + bool hidden_ = false; std::function callback_; }; From c9c34c4ca102f6bc4ee58030a0e04a8c3f627024 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 Sep 2019 11:43:44 -0700 Subject: [PATCH 13/33] SaveState: Handle compression thread better. Was leaking before. --- Core/SaveState.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index eaca6b42df..40e93d8ce5 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -152,11 +152,12 @@ namespace SaveState void ScheduleCompress(std::vector *result, const std::vector *state, const std::vector *base) { - auto th = new std::thread([=]{ + if (compressThread_.joinable()) + compressThread_.join(); + compressThread_ = std::thread([=]{ setCurrentThreadName("SaveStateCompress"); Compress(*result, *state, *base); }); - th->detach(); } void Compress(std::vector &result, const std::vector &state, const std::vector &base) @@ -207,6 +208,9 @@ namespace SaveState void Clear() { + if (compressThread_.joinable()) + compressThread_.join(); + // This lock is mainly for shutdown. std::lock_guard guard(lock_); first_ = 0; @@ -232,6 +236,7 @@ namespace SaveState StateBuffer bases_[2]; std::vector baseMapping_; std::mutex lock_; + std::thread compressThread_; int base_; int baseUsage_; From 0b9353b1dc5392b0bb45a4a74d4aa0d1718bf1bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 2 Oct 2019 22:03:02 +0200 Subject: [PATCH 14/33] Touchscreen: Apply weihouya's fix, add a touchscreen test screen. See #12381. --- UI/ControlMappingScreen.cpp | 85 +++++++++++++++++++ UI/ControlMappingScreen.h | 30 ++++++- UI/DevScreens.cpp | 1 + UI/GameSettingsScreen.cpp | 8 +- UI/GameSettingsScreen.h | 1 + .../src/org/ppsspp/ppsspp/NativeActivity.java | 1 + 6 files changed, 123 insertions(+), 3 deletions(-) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index b5575dbcae..eefb18a231 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -570,3 +570,88 @@ void AnalogTestScreen::CreateViews() { root_->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); } + +bool TouchTestScreen::touch(const TouchInput &touch) { + UIDialogScreenWithBackground::touch(touch); + if (touch.flags & TOUCH_DOWN) { + bool found = false; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touches_[i].id == touch.id) { + WLOG("Double touch"); + touches_[i].x = touch.x; + touches_[i].y = touch.y; + found = true; + } + } + if (!found) { + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touches_[i].id == -1) { + touches_[i].id = touch.id; + touches_[i].x = touch.x; + touches_[i].y = touch.y; + break; + } + } + } + } + if (touch.flags & TOUCH_MOVE) { + bool found = false; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touches_[i].id == touch.id) { + touches_[i].x = touch.x; + touches_[i].y = touch.y; + found = true; + } + } + if (!found) { + WLOG("Move without touch down: %d", touch.id); + } + } + if (touch.flags & TOUCH_UP) { + bool found = false; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touches_[i].id == touch.id) { + found = true; + touches_[i].id = -1; + break; + } + } + if (!found) { + WLOG("Touch release without touch down"); + } + } + return true; +} + +void TouchTestScreen::CreateViews() { + using namespace UI; + + I18NCategory *di = GetI18NCategory("Dialog"); + I18NCategory *gr = GetI18NCategory("Graphics"); + root_ = new LinearLayout(ORIENT_VERTICAL); + LinearLayout *theTwo = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)); + root_->Add(theTwo); + root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange); + root_->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); +} + +void TouchTestScreen::render() { + UIDialogScreenWithBackground::render(); + screenManager()->getUIContext()->BeginNoTex(); + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touches_[i].id != -1) { + screenManager()->getUIContext()->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0); + } + } + screenManager()->getUIContext()->Flush(); +} + +void RecreateActivity(); + +UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) { + System_SendMessage("immersive", ""); + if (g_Config.iAndroidHwScale != 0) { + RecreateActivity(); + } + return UI::EVENT_DONE; +} diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index a1292548ba..ed58f65fd9 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -105,6 +105,32 @@ public: protected: virtual void CreateViews() override; - UI::TextView *lastKeyEvent_; - UI::TextView *lastLastKeyEvent_; + UI::TextView *lastKeyEvent_ = nullptr; + UI::TextView *lastLastKeyEvent_ = nullptr; +}; + +class TouchTestScreen : public UIDialogScreenWithBackground { +public: + TouchTestScreen() { + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + touches_[i].id = -1; + } + } + + bool touch(const TouchInput &touch) override; + void render() override; + +protected: + struct TrackedTouch { + int id; + int x; + int y; + }; + enum { + MAX_TOUCH_POINTS = 10, + }; + TrackedTouch touches_[MAX_TOUCH_POINTS]{}; + + virtual void CreateViews() override; + UI::EventReturn OnImmersiveModeChange(UI::EventParams &e); }; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 35f9eb312f..684a293a43 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -42,6 +42,7 @@ #include "GPU/GPUState.h" #include "UI/MiscScreens.h" #include "UI/DevScreens.h" +#include "UI/ControlMappingScreen.h" #include "UI/GameSettingsScreen.h" #ifdef _WIN32 diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b1bf46f218..7645fc9b7e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -839,7 +839,7 @@ UI::EventReturn GameSettingsScreen::OnScreenRotation(UI::EventParams &e) { return UI::EVENT_DONE; } -static void RecreateActivity() { +void RecreateActivity() { const int SYSTEM_JELLYBEAN = 16; if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= SYSTEM_JELLYBEAN) { ILOG("Sending recreate"); @@ -1342,6 +1342,7 @@ void DeveloperToolsScreen::CreateViews() { if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest); } + list->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnTouchscreenTest); allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER); @@ -1443,6 +1444,11 @@ UI::EventReturn DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnTouchscreenTest(UI::EventParams &e) { + screenManager()->push(new TouchTestScreen()); + return UI::EVENT_DONE; +} + UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) { NativeMessageReceived("clear jit", ""); return UI::EVENT_DONE; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 8620336907..cde4e47409 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -173,6 +173,7 @@ private: UI::EventReturn OnJitDebugTools(UI::EventParams &e); UI::EventReturn OnRemoteDebugger(UI::EventParams &e); UI::EventReturn OnGPUDriverTest(UI::EventParams &e); + UI::EventReturn OnTouchscreenTest(UI::EventParams &e); bool allowDebugger_ = false; bool canAllowDebugger_ = true; diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 2a70b05e56..fa3d86f25e 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -578,6 +578,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); getDesiredBackbufferSize(desiredSize); // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. From 18edfefa0a78aed9feb27f390dd8177bf9d73f54 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Thu, 3 Oct 2019 06:50:24 +0100 Subject: [PATCH 15/33] SDL: ensure swap interval is set correctly for SDL2 kmsdrm driver It appears that SDL2's kmsdrm driver ignores the swap interval setting if the SDL context has not yet been created. Moving the call to after context creation allows it to work as expected. Fixes vsync when running ppsspp in a KMS context using the SDL2 kmsdrm driver (which is especially useful for Raspberry Pi 4 B, but is also needed for other systems including Raspberry Pi 3B via firmware KMS & Intel Haswell i965 via KMS on x64). --- SDL/SDLMain.cpp | 4 +++- headless/SDLHeadlessHost.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index ddf6e5cf20..28c3548201 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -443,7 +443,6 @@ int main(int argc, char *argv[]) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetSwapInterval(1); // Force fullscreen if the resolution is too low to run windowed. if (g_DesktopWidth < 480 * 2 && g_DesktopHeight < 272 * 2) { @@ -571,6 +570,9 @@ int main(int argc, char *argv[]) { NativeResized(); } + // Ensure that the swap interval is set after context creation (needed for kmsdrm) + SDL_GL_SetSwapInterval(1); + SDL_AudioSpec fmt, ret_fmt; memset(&fmt, 0, sizeof(fmt)); fmt.freq = 44100; diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index c5702eabc4..ac04878237 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -119,11 +119,13 @@ bool GLDummyGraphicsContext::InitFromRenderThread(std::string *errorMessage) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetSwapInterval(1); screen_ = CreateHiddenWindow(); glContext_ = SDL_GL_CreateContext(screen_); + // Ensure that the swap interval is set after context creation (needed for kmsdrm) + SDL_GL_SetSwapInterval(1); + #ifndef USING_GLES2 // Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc. // glewExperimental allows us to force GLEW to search for the pointers anyway. From b79f421b97a3070fde9444e17c4f301feaebda0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Oct 2019 12:02:13 +0200 Subject: [PATCH 16/33] Upgrade gradle version again --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6fe5d2708d..043e3571c5 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.1' } } From 349e64fe3111f6370a2b54831f54c11ad0704ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 3 Oct 2019 18:31:20 +0200 Subject: [PATCH 17/33] Improve the touchscreen test screen with some numbers. Getting closer to finding out what's wrong on the MOQI I7S when using the system fullscreen button... --- UI/ControlMappingScreen.cpp | 34 +++++++++++++++++++++++++++++++--- UI/ControlMappingScreen.h | 4 ++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index eefb18a231..4300729285 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -21,6 +21,7 @@ #include "base/colorutil.h" #include "base/logging.h" +#include "base/display.h" #include "i18n/i18n.h" #include "input/keycodes.h" #include "input/input_state.h" @@ -637,13 +638,40 @@ void TouchTestScreen::CreateViews() { void TouchTestScreen::render() { UIDialogScreenWithBackground::render(); - screenManager()->getUIContext()->BeginNoTex(); + UIContext *ui_context = screenManager()->getUIContext(); + ui_context->BeginNoTex(); for (int i = 0; i < MAX_TOUCH_POINTS; i++) { if (touches_[i].id != -1) { - screenManager()->getUIContext()->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0); + ui_context->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0); } } - screenManager()->getUIContext()->Flush(); + ui_context->Flush(); + + ui_context->Begin(); + + char buffer[1024]; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touches_[i].id != -1) { + ui_context->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0); + snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y); + ui_context->DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII); + } + } + + snprintf(buffer, sizeof(buffer), + "dp_res: %dx%d\n" + "pixel_res: %dx%d\n" + "g_dpi: %f\n" + "g_dpi_scale: %0.3fx%0.3f\n" + "g_dpi_scale_real: %0.3fx%0.3f\n", + dp_xres, dp_yres, + pixel_xres, pixel_yres, + g_dpi, + g_dpi_scale_x, g_dpi_scale_y, + g_dpi_scale_real_x, g_dpi_scale_real_y); + + ui_context->DrawTextShadow(buffer, 20.0f, dp_yres / 2.0f, 0xFFFFFFFF, ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); + ui_context->Flush(); } void RecreateActivity(); diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index ed58f65fd9..77f5c90df5 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -123,8 +123,8 @@ public: protected: struct TrackedTouch { int id; - int x; - int y; + float x; + float y; }; enum { MAX_TOUCH_POINTS = 10, From c023cd7e20a1702004d52721c62ff010425b8101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 5 Oct 2019 23:29:15 +0200 Subject: [PATCH 18/33] Make use of OnSystemUiVisibilityChangeListener to check whether navbar is hidden. Affects DPI calculations. Also recalculate all dp parameters after either display or buffer size changed. --- android/jni/app-android.cpp | 49 +++++++++++-------- .../src/org/ppsspp/ppsspp/NativeActivity.java | 30 +++++++----- .../src/org/ppsspp/ppsspp/NativeGLView.java | 2 - .../org/ppsspp/ppsspp/NativeSurfaceView.java | 2 + 4 files changed, 49 insertions(+), 34 deletions(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 1bebc69c87..1834e2b1a8 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -611,6 +611,30 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, NativeMessageReceived("recreateviews", ""); } +static void recalculateDpi() { + g_dpi = display_dpi_x; + g_dpi_scale_x = 240.0f / display_dpi_x; + g_dpi_scale_y = 240.0f / display_dpi_y; + g_dpi_scale_real_x = g_dpi_scale_x; + g_dpi_scale_real_y = g_dpi_scale_y; + + dp_xres = display_xres * g_dpi_scale_x; + dp_yres = display_yres * g_dpi_scale_y; + + // Touch scaling is from display pixels to dp pixels. + // Wait, doesn't even make sense... this is equal to g_dpi_scale_x. TODO: Figure out what's going on! + dp_xscale = (float)dp_xres / (float)display_xres; + dp_yscale = (float)dp_yres / (float)display_yres; + + pixel_in_dps_x = (float)pixel_xres / dp_xres; + pixel_in_dps_y = (float)pixel_yres / dp_yres; + + ILOG("RecalcDPI: g_dpi=%f g_dpi_scale_x=%f g_dpi_scale_y=%f", g_dpi, g_dpi_scale_x, g_dpi_scale_y); + ILOG("RecalcDPI: dp_xscale=%f dp_yscale=%f", dp_xscale, dp_yscale); + ILOG("RecalcDPI: dp_xres=%d dp_yres=%d", dp_xres, dp_yres); + ILOG("RecalcDPI: pixel_xres=%d pixel_yres=%d", pixel_xres, pixel_yres); +} + extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv *, jclass, jint bufw, jint bufh, jint format) { ILOG("NativeApp.backbufferResize(%d x %d)", bufw, bufh); @@ -622,32 +646,13 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv pixel_yres = bufh; backbuffer_format = format; - g_dpi = display_dpi_x; - g_dpi_scale_x = 240.0f / g_dpi; - g_dpi_scale_y = 240.0f / g_dpi; - g_dpi_scale_real_x = g_dpi_scale_x; - g_dpi_scale_real_y = g_dpi_scale_y; - - dp_xres = display_xres * g_dpi_scale_x; - dp_yres = display_yres * g_dpi_scale_y; - - // Touch scaling is from display pixels to dp pixels. - dp_xscale = (float)dp_xres / (float)display_xres; - dp_yscale = (float)dp_yres / (float)display_yres; - - pixel_in_dps_x = (float)pixel_xres / dp_xres; - pixel_in_dps_y = (float)pixel_yres / dp_yres; - - ILOG("g_dpi=%f g_dpi_scale_x=%f g_dpi_scale_y=%f", g_dpi, g_dpi_scale_x, g_dpi_scale_y); - ILOG("dp_xscale=%f dp_yscale=%f", dp_xscale, dp_yscale); - ILOG("dp_xres=%d dp_yres=%d", dp_xres, dp_yres); - ILOG("pixel_xres=%d pixel_yres=%d", pixel_xres, pixel_yres); + recalculateDpi(); if (new_size) { ILOG("Size change detected (previously %d,%d) - calling NativeResized()", old_w, old_h); NativeResized(); } else { - ILOG("NativeApp::backbufferReisze: Size didn't change."); + ILOG("NativeApp::backbufferResize: Size didn't change."); } } @@ -897,6 +902,8 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JN display_dpi_x = dpi; display_dpi_y = dpi; display_hz = refreshRate; + + recalculateDpi(); } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() { diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index fa3d86f25e..1dc3e7ba65 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -53,6 +53,8 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Locale; +import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + public abstract class NativeActivity extends Activity implements SurfaceHolder.Callback { // Remember to loadLibrary your JNI .so in a static {} block @@ -85,6 +87,8 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private boolean sustainedPerfSupported; + private boolean navigationHidden; + // audioFocusChangeListener to listen to changes in audio state private AudioFocusChangeListener audioFocusChangeListener; private AudioManager audioManager; @@ -404,18 +408,20 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C @SuppressLint("InlinedApi") @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void updateSystemUiVisibility() { + // Compute our _desired_ systemUiVisibility int flags = 0; if (useLowProfileButtons()) { flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE; } if (useImmersive()) { - flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | SYSTEM_UI_FLAG_HIDE_NAVIGATION; } if (getWindow().getDecorView() != null) { getWindow().getDecorView().setSystemUiVisibility(flags); } else { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring"); } + updateDisplayMeasurements(); } @@ -457,16 +463,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C Display display = getWindowManager().getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); - if (useImmersive() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !isInMultiWindowMode()) { - display.getRealMetrics(metrics); - } else { - // multi-window mode - display.getMetrics(metrics); - } + if (navigationHidden) { + display.getRealMetrics(metrics); } else { display.getMetrics(metrics); } + densityDpi = metrics.densityDpi; refreshRate = display.getRefreshRate(); @@ -602,6 +604,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C return; } Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); + // The window size might have changed (immersive mode, native fullscreen on some devices) NativeApp.backbufferResize(width, height, format); mSurface = holder.getSurface(); if (!javaGL) { @@ -671,9 +674,14 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { @Override public void onSystemUiVisibilityChange(int visibility) { - if (visibility == 0) { - updateSystemUiVisibility(); - } + // Called when the system UI's visibility changes, regardless of + // whether it's because of our or system actions. + // We will try to force it to follow our preference but will not stupidly + // act as if it's visible if it's not. + navigationHidden = ((visibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); + // TODO: Check here if it's the state we want. + Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); + updateDisplayMeasurements(); } }); } diff --git a/android/src/org/ppsspp/ppsspp/NativeGLView.java b/android/src/org/ppsspp/ppsspp/NativeGLView.java index af5b738be6..901630e045 100644 --- a/android/src/org/ppsspp/ppsspp/NativeGLView.java +++ b/android/src/org/ppsspp/ppsspp/NativeGLView.java @@ -116,7 +116,6 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, @Override public void onPause() { - Log.i(TAG, "onPause"); super.onPause(); mSensorManager.unregisterListener(this); if (mController != null) { @@ -131,7 +130,6 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); if (mController != null) { mController.onResume(); - // According to the docs, the Moga's state can be inconsistent here. // We should do a one time poll. TODO } diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index 1efc71fd8e..e855a3212c 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -3,6 +3,7 @@ package org.ppsspp.ppsspp; // Touch- and sensor-enabled SurfaceView. // Supports simple multitouch and pressure. // DPI scaling is handled by the native code. +// Used by the Vulkan backend (and EGL, but that path is no longer active) import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -115,6 +116,7 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene } public void onPause() { + Log.i(TAG, "onPause"); mSensorManager.unregisterListener(this); if (mController != null) { mController.onPause(); From 0698944752d3e676a1a0161213b7ec9c9a902cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 6 Oct 2019 00:20:49 +0200 Subject: [PATCH 19/33] Fixes --- android/jni/AndroidVulkanContext.cpp | 4 ++-- android/jni/app-android.cpp | 2 ++ android/src/org/ppsspp/ppsspp/NativeActivity.java | 12 +++++------- ext/native/ui/screen.cpp | 1 + 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index b514f13d6a..71c63077b6 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -211,7 +211,7 @@ void AndroidVulkanContext::SwapBuffers() { } void AndroidVulkanContext::Resize() { - ILOG("AndroidVulkanContext::Resize begin (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + ILOG("AndroidVulkanContext::Resize begin (oldsize: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); g_Vulkan->DestroyObjects(); @@ -220,7 +220,7 @@ void AndroidVulkanContext::Resize() { g_Vulkan->ReinitSurface(); g_Vulkan->InitObjects(); draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); - ILOG("AndroidVulkanContext::Resize end (%d, %d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); + ILOG("AndroidVulkanContext::Resize end (final size: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } void AndroidVulkanContext::SwapInterval(int interval) { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 1834e2b1a8..2dfd001858 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -629,6 +629,7 @@ static void recalculateDpi() { pixel_in_dps_x = (float)pixel_xres / dp_xres; pixel_in_dps_y = (float)pixel_yres / dp_yres; + ILOG("RecalcDPI: display_xres=%d display_yres=%d", display_xres, display_yres); ILOG("RecalcDPI: g_dpi=%f g_dpi_scale_x=%f g_dpi_scale_y=%f", g_dpi, g_dpi_scale_x, g_dpi_scale_y); ILOG("RecalcDPI: dp_xscale=%f dp_yscale=%f", dp_xscale, dp_yscale); ILOG("RecalcDPI: dp_xres=%d dp_yres=%d", dp_xres, dp_yres); @@ -904,6 +905,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JN display_hz = refreshRate; recalculateDpi(); + NativeResized(); } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() { diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 1dc3e7ba65..312955e298 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -53,8 +53,6 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Locale; -import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - public abstract class NativeActivity extends Activity implements SurfaceHolder.Callback { // Remember to loadLibrary your JNI .so in a static {} block @@ -414,14 +412,13 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE; } if (useImmersive()) { - flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; } if (getWindow().getDecorView() != null) { getWindow().getDecorView().setSystemUiVisibility(flags); } else { - Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring"); + Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring for now"); } - updateDisplayMeasurements(); } @@ -606,6 +603,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); // The window size might have changed (immersive mode, native fullscreen on some devices) NativeApp.backbufferResize(width, height, format); + updateDisplayMeasurements(); mSurface = holder.getSurface(); if (!javaGL) { // If we got a surface, this starts the thread. If not, it doesn't. @@ -678,7 +676,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // whether it's because of our or system actions. // We will try to force it to follow our preference but will not stupidly // act as if it's visible if it's not. - navigationHidden = ((visibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); + navigationHidden = ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); // TODO: Check here if it's the state we want. Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); updateDisplayMeasurements(); @@ -812,7 +810,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateSystemUiVisibility(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - densityDpi = (float) newConfig.densityDpi; + densityDpi = (float)newConfig.densityDpi; } } diff --git a/ext/native/ui/screen.cpp b/ext/native/ui/screen.cpp index 8d86f10a6b..3bdfb5cb9f 100644 --- a/ext/native/ui/screen.cpp +++ b/ext/native/ui/screen.cpp @@ -125,6 +125,7 @@ void ScreenManager::deviceRestored() { } void ScreenManager::resized() { + ILOG("ScreenManager::resized(dp: %dx%d)", dp_xres, dp_yres); std::lock_guard guard(inputLock_); // Have to notify the whole stack, otherwise there will be problems when going back // to non-top screens. From d137f35c64d9c77f2ffb1e7e49504750f2d96dfd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 5 Oct 2019 16:21:13 -0700 Subject: [PATCH 20/33] Android: Setup UI callback before settings change. We need to receive a notification of our own change, or the initial flag will be out of sync. Also adds it in the javaGL path. --- .../src/org/ppsspp/ppsspp/NativeActivity.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 312955e298..2f6ffa029a 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -86,6 +86,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private boolean sustainedPerfSupported; private boolean navigationHidden; + private View navigationCallbackView = null; // audioFocusChangeListener to listen to changes in audio state private AudioFocusChangeListener audioFocusChangeListener; @@ -406,6 +407,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C @SuppressLint("InlinedApi") @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void updateSystemUiVisibility() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + setupSystemUiCallback(); + } + // Compute our _desired_ systemUiVisibility int flags = 0; if (useLowProfileButtons()) { @@ -414,8 +419,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C if (useImmersive()) { flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; } - if (getWindow().getDecorView() != null) { - getWindow().getDecorView().setSystemUiVisibility(flags); + + View decorView = getWindow().peekDecorView(); + if (decorView != null) { + decorView.setSystemUiVisibility(flags); } else { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring for now"); } @@ -538,9 +545,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - setupSystemUiCallback(); - } } mSurfaceView = new NativeSurfaceView(NativeActivity.this); @@ -669,7 +673,12 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C @TargetApi(Build.VERSION_CODES.KITKAT) void setupSystemUiCallback() { - getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { + View decorView = getWindow().peekDecorView(); + if (decorView == null || decorView == navigationCallbackView) { + return; + } + + decorView.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { @Override public void onSystemUiVisibilityChange(int visibility) { // Called when the system UI's visibility changes, regardless of @@ -682,6 +691,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateDisplayMeasurements(); } }); + navigationCallbackView = decorView; } @Override @@ -727,6 +737,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C NativeApp.shutdown(); initialized = false; } + navigationCallbackView = null; } @Override From f342d8d8565a01b06c853aef56d45836132d24fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 6 Oct 2019 13:21:57 +0200 Subject: [PATCH 21/33] Logging improvements --- UI/ControlMappingScreen.cpp | 11 +++++++++++ UI/NativeApp.cpp | 2 ++ android/jni/app-android.cpp | 5 +++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 4300729285..6575d45cc9 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -636,6 +636,11 @@ void TouchTestScreen::CreateViews() { root_->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); } +#if PPSSPP_PLATFORM(ANDROID) +extern int display_xres; +extern int display_yres; +#endif + void TouchTestScreen::render() { UIDialogScreenWithBackground::render(); UIContext *ui_context = screenManager()->getUIContext(); @@ -659,11 +664,17 @@ void TouchTestScreen::render() { } snprintf(buffer, sizeof(buffer), +#if PPSSPP_PLATFORM(ANDROID) + "display_res: %dx%d\n" +#endif "dp_res: %dx%d\n" "pixel_res: %dx%d\n" "g_dpi: %f\n" "g_dpi_scale: %0.3fx%0.3f\n" "g_dpi_scale_real: %0.3fx%0.3f\n", +#if PPSSPP_PLATFORM(ANDROID) + display_xres, display_yres, +#endif dp_xres, dp_yres, pixel_xres, pixel_yres, g_dpi, diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 78ff472892..b4719f5900 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1006,6 +1006,7 @@ void NativeRender(GraphicsContext *graphicsContext) { } if (resized) { + ILOG("Resized flag set - recalculating bounds"); resized = false; if (uiContext) { @@ -1275,6 +1276,7 @@ void NativeMessageReceived(const char *message, const char *value) { void NativeResized() { // NativeResized can come from any thread so we just set a flag, then process it later. if (g_graphicsInited) { + ILOG("NativeResized - setting flag"); resized = true; } else { ILOG("NativeResized ignored, not initialized"); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 2dfd001858..1f9655b3a7 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -101,8 +101,9 @@ static int deviceType; // Should only be used for display detection during startup (for config defaults etc) // This is the ACTUAL display size, not the hardware scaled display size. -static int display_xres; -static int display_yres; +// Exposed so it can be displayed on the touchscreen test. +int display_xres; +int display_yres; static int display_dpi_x; static int display_dpi_y; static int backbuffer_format; // Android PixelFormat enum From ccda27edcf908898490d4a26c5bd0246d813bc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 6 Oct 2019 13:22:14 +0200 Subject: [PATCH 22/33] Increase max aspect to 2.4. Fixes #12349 --- android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 29b3bd3f75..74dc92743a 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -40,7 +40,7 @@ android:logo="@drawable/ic_banner" android:isGame="true" android:banner="@drawable/tv_banner"> - + Date: Sun, 6 Oct 2019 13:25:10 +0200 Subject: [PATCH 23/33] Android: Preparation for future cutout (notch) support --- android/res/values-v28/styles.xml | 13 ++++++++ .../src/org/ppsspp/ppsspp/NativeActivity.java | 30 ++++++++++++++++++- .../src/org/ppsspp/ppsspp/NativeGLView.java | 2 -- .../org/ppsspp/ppsspp/NativeSurfaceView.java | 2 -- 4 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 android/res/values-v28/styles.xml diff --git a/android/res/values-v28/styles.xml b/android/res/values-v28/styles.xml new file mode 100644 index 0000000000..19a29a3a42 --- /dev/null +++ b/android/res/values-v28/styles.xml @@ -0,0 +1,13 @@ + + + #FF000000 + + diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 2f6ffa029a..a1d38ecdf3 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -31,6 +31,7 @@ import android.text.InputType; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import android.view.DisplayCutout; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.InputDevice; @@ -119,6 +120,11 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private int pixelWidth; private int pixelHeight; + private int safeInsetLeft = 0; + private int safeInsetRight = 0; + private int safeInsetTop = 0; + private int safeInsetBottom = 0; + private static final String[] permissionsForStorage = { Manifest.permission.WRITE_EXTERNAL_STORAGE, }; @@ -673,7 +679,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C @TargetApi(Build.VERSION_CODES.KITKAT) void setupSystemUiCallback() { - View decorView = getWindow().peekDecorView(); + final View decorView = getWindow().peekDecorView(); if (decorView == null || decorView == navigationCallbackView) { return; } @@ -688,6 +694,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C navigationHidden = ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); // TODO: Check here if it's the state we want. Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); + Log.i(TAG, "decorView: " + decorView.getWidth() + "x" + decorView.getHeight()); updateDisplayMeasurements(); } }); @@ -813,6 +820,27 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } + @Override + public void onAttachedToWindow() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + Log.i(TAG, "onAttachedToWindow"); + DisplayCutout cutout = getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); + if (cutout != null) { + safeInsetLeft = cutout.getSafeInsetLeft(); + safeInsetRight = cutout.getSafeInsetRight(); + safeInsetTop = cutout.getSafeInsetTop(); + safeInsetBottom = cutout.getSafeInsetBottom(); + Log.i(TAG, "Safe insets: left: " + safeInsetLeft + " right: " + safeInsetRight + " top: " + safeInsetTop + " bottom: " + safeInsetBottom); + } else { + Log.i(TAG, "Cutout was null"); + safeInsetLeft = 0; + safeInsetRight = 0; + safeInsetTop = 0; + safeInsetBottom = 0; + } + } + } + @Override public void onConfigurationChanged(Configuration newConfig) { Log.i(TAG, "onConfigurationChanged"); diff --git a/android/src/org/ppsspp/ppsspp/NativeGLView.java b/android/src/org/ppsspp/ppsspp/NativeGLView.java index 901630e045..b56380358d 100644 --- a/android/src/org/ppsspp/ppsspp/NativeGLView.java +++ b/android/src/org/ppsspp/ppsspp/NativeGLView.java @@ -125,7 +125,6 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, @Override public void onResume() { - Log.i(TAG, "onResume"); super.onResume(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); if (mController != null) { @@ -136,7 +135,6 @@ public class NativeGLView extends GLSurfaceView implements SensorEventListener, } public void onDestroy() { - Log.i(TAG, "onDestroy"); if (mController != null) { mController.exit(); } diff --git a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java index e855a3212c..01686be52f 100644 --- a/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java +++ b/android/src/org/ppsspp/ppsspp/NativeSurfaceView.java @@ -116,7 +116,6 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene } public void onPause() { - Log.i(TAG, "onPause"); mSensorManager.unregisterListener(this); if (mController != null) { mController.onPause(); @@ -133,7 +132,6 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene } public void onDestroy() { - Log.i(TAG, "onDestroy"); if (mController != null) { mController.exit(); } From f65258fc6015a52dad7005f1e4ce025ae090d3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 6 Oct 2019 13:25:44 +0200 Subject: [PATCH 24/33] Android: Finally fix our display sizing problems for good, hopefully. --- .../src/org/ppsspp/ppsspp/NativeActivity.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index a1d38ecdf3..55c5d9b192 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -468,17 +468,33 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C sz.y = NativeApp.getDesiredBackbufferHeight(); } + private SurfaceView getSurfaceView() { + if (mGLSurfaceView != null) { + return mGLSurfaceView; + } else { + return mSurfaceView; + } + } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public void updateDisplayMeasurements() { Display display = getWindowManager().getDefaultDisplay(); + // Early in startup, we don't have a view to query. Do our best to get some kind of size + // that can be used by config default heuristics, and so on. DisplayMetrics metrics = new DisplayMetrics(); - if (navigationHidden) { + if (navigationHidden && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { display.getRealMetrics(metrics); } else { display.getMetrics(metrics); } + // Later on, we have the exact pixel size so let's just use it. + SurfaceView view = getSurfaceView(); + if (view != null) { + metrics.widthPixels = view.getWidth(); + metrics.heightPixels = view.getHeight(); + } densityDpi = metrics.densityDpi; refreshRate = display.getRefreshRate(); From bf1777f7d3702e6a0f71c7ec1fc51976e23c2327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 6 Oct 2019 15:02:44 +0200 Subject: [PATCH 25/33] 1.9.3 commit --- README.md | 7 ++++++- android/AndroidManifest.xml | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9fe7f9dd2..aa36a2b225 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,12 @@ 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.9.0/1.9.1 +What's new in 1.9.0/1.9.3 +------------------------- +* Sizing/touch fixes on Android +* Fixes for some crashes + +What's new in 1.9.0/1.9.2 ------------------------- * Flicker fixed in God of War that appeared with newer drivers for Mali GPUs (#12224) * Improve performance of God of War on Vulkan (#12242), implement built-in GPU profiler (#12262, #12266) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 74dc92743a..435ee9c4db 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="19030000" + android:versionName="1.9.3.0"> From 78571b8e37c66acbda461f12b27203b35557b9ca Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 Oct 2019 08:30:39 -0700 Subject: [PATCH 26/33] http: Don't treat http:// as a relative path. --- Common/FileUtil.cpp | 3 +++ UI/Store.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 7b9e8259b7..04ae730c1c 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -136,6 +136,9 @@ static bool ResolvePathVista(const std::wstring &path, wchar_t *buf, DWORD bufSi #endif std::string ResolvePath(const std::string &path) { + if (startsWith(path, "http://") || startsWith(path, "https://")) { + return path; + } #ifdef _WIN32 static const int BUF_SIZE = 32768; wchar_t *buf = new wchar_t[BUF_SIZE] {}; diff --git a/UI/Store.cpp b/UI/Store.cpp index f403505c65..254843afa3 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -43,7 +43,7 @@ std::string ResolveUrl(std::string baseUrl, std::string url) { return baseUrl; } else if (url[0] == '/') { return baseUrl + url.substr(1); - } else if (url.substr(0, 7) == "http://") { + } else if (startsWith(url, "http://") || startsWith(url, "https://")) { return url; } else { // Huh. From 3e12734b80f5dac46d481396784d9a2aead8ba21 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 Oct 2019 08:50:35 -0700 Subject: [PATCH 27/33] http: Use relative paths for remote isos. --- UI/RemoteISOScreen.cpp | 49 +++++++++++++++++++++--------------------- UI/RemoteISOScreen.h | 2 ++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 6dd15835f6..bae96cefff 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -24,6 +24,7 @@ #include "json/json_reader.h" #include "net/http_client.h" #include "net/resolve.h" +#include "net/url.h" #include "Common/Common.h" #include "Core/Config.h" #include "Core/WebServer.h" @@ -39,13 +40,7 @@ static bool scanAborted = false; static std::string RemoteSubdir() { if (g_Config.bRemoteISOManual) { - std::string subdir = g_Config.sRemoteISOSubdir; - size_t offset = subdir.find_last_of("/"); - if (offset != subdir.length() - 1 && offset != subdir.npos) { - // Truncate everything after last / - subdir.erase(offset + 1); - } - return subdir; + return g_Config.sRemoteISOSubdir; } return "/"; @@ -142,9 +137,6 @@ static bool FindServer(std::string &resultHost, int &resultPort) { const char *host = entry.getString("ip", ""); int port = entry.getInt("p", 0); - char url[1024] = {}; - snprintf(url, sizeof(url), "http://%s:%d", host, port); - if (TryServer(host, port)) { return true; } @@ -154,23 +146,26 @@ static bool FindServer(std::string &resultHost, int &resultPort) { return false; } -static bool LoadGameList(const std::string &host, int port, std::vector &games) { +bool LoadRemoteGameList(const std::string &url, bool *cancel, std::vector &games) { http::Client http; Buffer result; int code = 500; std::vector responseHeaders; - // TODO: Use relative url parsing (URL::Relative) instead. - std::string subdir = RemoteSubdir(); + + Url baseURL(url); + if (!baseURL.Valid()) { + return false; + } // Start by requesting the list of games from the server. - if (http.Resolve(host.c_str(), port)) { - if (http.Connect(2, 20.0, &scanCancelled)) { - code = http.GET(subdir.c_str(), &result, responseHeaders); + if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) { + if (http.Connect(2, 20.0, cancel)) { + code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders); http.Disconnect(); } } - if (code != 200 || scanCancelled) { + if (code != 200 || (cancel && *cancel)) { return false; } @@ -196,7 +191,6 @@ static bool LoadGameList(const std::string &host, int port, std::vector &games) { + std::string subdir = RemoteSubdir(); + + char temp[1024]; + snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s", host.c_str(), port, subdir.c_str()); + + LoadRemoteGameList(temp, &scanCancelled, games); + // Save for next time unless manual is true if (!games.empty() && !g_Config.bRemoteISOManual) { g_Config.sLastRemoteISOServer = host; @@ -592,7 +593,7 @@ UI::EventReturn RemoteISOSettingsScreen::OnChangeRemoteISOSubdir(UI::EventParams ReplaceAll(g_Config.sRemoteISOSubdir, " ", "%20"); ReplaceAll(g_Config.sRemoteISOSubdir, "\\", "/"); //Make sure it begins with / - if (g_Config.sRemoteISOSubdir[0] != '/') + if (g_Config.sRemoteISOSubdir.empty() || g_Config.sRemoteISOSubdir[0] != '/') g_Config.sRemoteISOSubdir = "/" + g_Config.sRemoteISOSubdir; return UI::EVENT_DONE; diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index b7f7ff0f5c..9a4ca9fde7 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -25,6 +25,8 @@ #include "UI/MiscScreens.h" #include "UI/MainScreen.h" +bool LoadRemoteGameList(const std::string &url, bool *cancel, std::vector &games); + class RemoteISOScreen : public UIScreenWithBackground { public: RemoteISOScreen(); From 2d7ce0afa3e1965d5795b5f7ad7f3d232e098660 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 Oct 2019 09:31:06 -0700 Subject: [PATCH 28/33] UI: Allow remote paths to be pinned. Currently, the listing request is synchronous, but it works fine otherwise. --- Core/Config.cpp | 5 +- UI/MainScreen.cpp | 2 +- UI/RemoteISOScreen.cpp | 82 ++++------------------------ UI/RemoteISOScreen.h | 2 - ext/native/file/path.cpp | 114 +++++++++++++++++++++++++++++++++++++-- ext/native/file/path.h | 2 +- 6 files changed, 126 insertions(+), 81 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 200c316029..e2142ce102 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1141,8 +1141,9 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { vPinnedPaths.clear(); for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) { // Unpin paths that are deleted automatically. - if (File::Exists(it->second)) { - vPinnedPaths.push_back(File::ResolvePath(it->second)); + const std::string &path = it->second; + if (startsWith(path, "http://") || startsWith(path, "https://") || File::Exists(path)) { + vPinnedPaths.push_back(File::ResolvePath(path)); } } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index e2028f5fda..d03c01e6ae 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -577,7 +577,7 @@ void GameBrowser::Refresh() { if (!isGame && !isSaveData) { if (allowBrowsing_) { - dirButtons.push_back(new DirButton(fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT))); + dirButtons.push_back(new DirButton(fileInfo[i].fullName, fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT))); } } else { gameButtons.push_back(new GameButton(fileInfo[i].fullName, *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT))); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index bae96cefff..8cf0a19ab9 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -20,6 +20,7 @@ #include #include "base/timeutil.h" +#include "file/path.h" #include "i18n/i18n.h" #include "json/json_reader.h" #include "net/http_client.h" @@ -146,84 +147,21 @@ static bool FindServer(std::string &resultHost, int &resultPort) { return false; } -bool LoadRemoteGameList(const std::string &url, bool *cancel, std::vector &games) { - http::Client http; - Buffer result; - int code = 500; - std::vector responseHeaders; - - Url baseURL(url); - if (!baseURL.Valid()) { - return false; - } - - // Start by requesting the list of games from the server. - if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) { - if (http.Connect(2, 20.0, cancel)) { - code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders); - http.Disconnect(); - } - } - - if (code != 200 || (cancel && *cancel)) { - return false; - } - - std::string listing; - std::vector items; - result.TakeAll(&listing); - - std::string contentType; - for (const std::string &header : responseHeaders) { - if (startsWithNoCase(header, "Content-Type:")) { - contentType = header.substr(strlen("Content-Type:")); - // Strip any whitespace (TODO: maybe move this to stringutil?) - contentType.erase(0, contentType.find_first_not_of(" \t\r\n")); - contentType.erase(contentType.find_last_not_of(" \t\r\n") + 1); - } - } - - // TODO: Technically, "TExt/hTml ; chaRSet = Utf8" should pass, but "text/htmlese" should not. - // But unlikely that'll be an issue. - bool parseHtml = startsWithNoCase(contentType, "text/html"); - bool parseText = startsWithNoCase(contentType, "text/plain"); - - if (parseText) { - // Plain text format - easy. - SplitString(listing, '\n', items); - } else if (parseHtml) { - // Try to extract from an automatic webserver directory listing... - GetQuotedStrings(listing, items); - } else { - ERROR_LOG(FILESYS, "Unsupported Content-Type: %s", contentType.c_str()); - return false; - } - - for (std::string item : items) { - // Apply some workarounds. - if (item.empty()) - continue; - - if (item.back() == '\r') - item.pop_back(); - - if (!RemoteISOFileSupported(item)) { - continue; - } - - games.push_back(baseURL.Relative(item).ToString()); - } - - return !games.empty(); -} - static bool LoadGameList(const std::string &host, int port, std::vector &games) { std::string subdir = RemoteSubdir(); char temp[1024]; snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s", host.c_str(), port, subdir.c_str()); - LoadRemoteGameList(temp, &scanCancelled, games); + PathBrowser browser(temp); + std::vector files; + browser.GetListing(files, "iso:cso:pbp:elf:prx:ppdmp:", &scanCancelled); + if (scanCancelled) { + return false; + } + for (auto &file : files) { + games.push_back(file.fullName); + } // Save for next time unless manual is true if (!games.empty() && !g_Config.bRemoteISOManual) { diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index 9a4ca9fde7..b7f7ff0f5c 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -25,8 +25,6 @@ #include "UI/MiscScreens.h" #include "UI/MainScreen.h" -bool LoadRemoteGameList(const std::string &url, bool *cancel, std::vector &games); - class RemoteISOScreen : public UIScreenWithBackground { public: RemoteISOScreen(); diff --git a/ext/native/file/path.cpp b/ext/native/file/path.cpp index d50a64f237..ced159863d 100644 --- a/ext/native/file/path.cpp +++ b/ext/native/file/path.cpp @@ -1,4 +1,107 @@ +#include +#include +#include "base/stringutil.h" #include "file/path.h" +#include "net/http_client.h" +#include "net/url.h" + +bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector &files, const char *filter) { + http::Client http; + Buffer result; + int code = 500; + std::vector responseHeaders; + + Url baseURL(url); + if (!baseURL.Valid()) { + return false; + } + + // Start by requesting the list of files from the server. + if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) { + if (http.Connect(2, 20.0, cancel)) { + code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders); + http.Disconnect(); + } + } + + if (code != 200 || (cancel && *cancel)) { + return false; + } + + std::string listing; + std::vector items; + result.TakeAll(&listing); + + std::string contentType; + for (const std::string &header : responseHeaders) { + if (startsWithNoCase(header, "Content-Type:")) { + contentType = header.substr(strlen("Content-Type:")); + // Strip any whitespace (TODO: maybe move this to stringutil?) + contentType.erase(0, contentType.find_first_not_of(" \t\r\n")); + contentType.erase(contentType.find_last_not_of(" \t\r\n") + 1); + } + } + + // TODO: Technically, "TExt/hTml ; chaRSet = Utf8" should pass, but "text/htmlese" should not. + // But unlikely that'll be an issue. + bool parseHtml = startsWithNoCase(contentType, "text/html"); + bool parseText = startsWithNoCase(contentType, "text/plain"); + + if (parseText) { + // Plain text format - easy. + SplitString(listing, '\n', items); + } else if (parseHtml) { + // Try to extract from an automatic webserver directory listing... + GetQuotedStrings(listing, items); + } else { + ELOG("Unsupported Content-Type: %s", contentType.c_str()); + return false; + } + + std::set filters; + if (filter) { + std::string tmp; + while (*filter) { + if (*filter == ':') { + filters.insert(std::move(tmp)); + } else { + tmp.push_back(*filter); + } + filter++; + } + if (!tmp.empty()) + filters.insert(std::move(tmp)); + } + + for (std::string item : items) { + // Apply some workarounds. + if (item.empty()) + continue; + if (item.back() == '\r') + item.pop_back(); + + FileInfo info; + info.name = item; + info.fullName = baseURL.Relative(item).ToString(); + info.isDirectory = endsWith(item, "/"); + info.exists = true; + info.size = 0; + info.isWritable = false; + if (!info.isDirectory) { + std::string ext = getFileExtension(info.fullName); + if (filter) { + if (filters.find(ext) == filters.end()) + continue; + } + } + + files.push_back(info); + } + + + std::sort(files.begin(), files.end()); + return !files.empty(); +} // Normalize slashes. void PathBrowser::SetPath(const std::string &path) { @@ -14,7 +117,7 @@ void PathBrowser::SetPath(const std::string &path) { path_ += "/"; } -void PathBrowser::GetListing(std::vector &fileInfo, const char *filter) { +bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter, bool *cancel) { #ifdef _WIN32 if (path_ == "/") { // Special path that means root of file system. @@ -34,7 +137,12 @@ void PathBrowser::GetListing(std::vector &fileInfo, const char *filter } #endif - getFilesInDir(path_.c_str(), &fileInfo, filter); + if (startsWith(path_, "http://") || startsWith(path_, "https://")) { + return LoadRemoteFileList(path_, cancel, fileInfo, filter); + } else { + getFilesInDir(path_.c_str(), &fileInfo, filter); + return true; + } } // TODO: Support paths like "../../hello" @@ -52,7 +160,7 @@ void PathBrowser::Navigate(const std::string &path) { path_ = path_.substr(0, slash + 1); } } else { - if (path[1] == ':' && path_ == "/") + if (path.size() > 2 && path[1] == ':' && path_ == "/") path_ = path; else path_ = path_ + path; diff --git a/ext/native/file/path.h b/ext/native/file/path.h index b54e9be494..fe4b78b776 100644 --- a/ext/native/file/path.h +++ b/ext/native/file/path.h @@ -16,7 +16,7 @@ public: PathBrowser(std::string path) { SetPath(path); } void SetPath(const std::string &path); - void GetListing(std::vector &fileInfo, const char *filter = 0); + bool GetListing(std::vector &fileInfo, const char *filter = nullptr, bool *cancel = nullptr); void Navigate(const std::string &path); std::string GetPath() const { From 8d3da2ce8881e16d93712ac8cc933e8f2a314c0a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 Oct 2019 09:34:23 -0700 Subject: [PATCH 29/33] UI: Cleanup browse button check. --- UI/MainScreen.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index d03c01e6ae..5df357ba96 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -526,11 +526,11 @@ void GameBrowser::Refresh() { if (allowBrowsing_) { topBar->Add(new Spacer(2.0f)); topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); -#if defined(USING_WIN_UI) || defined(USING_QT_UI) - topBar->Add(new Choice(mm->T("Browse", "Browse..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); -#else - topBar->Add(new Choice(mm->T("Home"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); -#endif + if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { + topBar->Add(new Choice(mm->T("Browse", "Browse..."), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); + } else { + topBar->Add(new Choice(mm->T("Home"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::HomeClick); + } } else { topBar->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); } From e5eb849e8bc87dcebcfb128638ab28343b3e336c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 Oct 2019 11:49:35 -0700 Subject: [PATCH 30/33] UI: Handle remote browsing asynchronously. --- UI/MainScreen.cpp | 21 ++++-- UI/MainScreen.h | 9 ++- UI/RemoteISOScreen.cpp | 4 +- ext/native/file/path.cpp | 142 +++++++++++++++++++++++++++++++-------- ext/native/file/path.h | 16 +++++ 5 files changed, 157 insertions(+), 35 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 5df357ba96..364e552f9d 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -441,7 +441,7 @@ void DirButton::Draw(UIContext &dc) { } GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags, UI::LayoutParams *layoutParams) - : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), gameList_(0), path_(path), gridStyle_(gridStyle), allowBrowsing_(allowBrowsing), lastText_(lastText), lastLink_(lastLink), flags_(flags) { + : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), allowBrowsing_(allowBrowsing), lastText_(lastText), lastLink_(lastLink), flags_(flags) { using namespace UI; Refresh(); } @@ -510,10 +510,17 @@ bool GameBrowser::HasSpecialFiles(std::vector &filenames) { return false; } +void GameBrowser::Update() { + LinearLayout::Update(); + if (listingPending_ && path_.IsListingReady()) { + Refresh(); + } +} + void GameBrowser::Refresh() { using namespace UI; - homebrewStoreButton_ = 0; + homebrewStoreButton_ = nullptr; // Kill all the contents Clear(); @@ -556,12 +563,14 @@ void GameBrowser::Refresh() { std::vector dirButtons; std::vector gameButtons; + listingPending_ = !path_.IsListingReady(); + std::vector filenames; if (HasSpecialFiles(filenames)) { for (size_t i = 0; i < filenames.size(); i++) { gameButtons.push_back(new GameButton(filenames[i], *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT))); } - } else { + } else if (!listingPending_) { std::vector fileInfo; path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:ppdmp:"); for (size_t i = 0; i < fileInfo.size(); i++) { @@ -616,6 +625,10 @@ void GameBrowser::Refresh() { } } + if (listingPending_) { + gameList_->Add(new UI::TextView(mm->T("Loading..."), ALIGN_CENTER, false, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT))); + } + for (size_t i = 0; i < dirButtons.size(); i++) { gameList_->Add(dirButtons[i])->OnClick.Handle(this, &GameBrowser::NavigateClick); } @@ -645,7 +658,7 @@ void GameBrowser::Refresh() { Add(new Spacer()); homebrewStoreButton_ = Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT))); } else { - homebrewStoreButton_ = 0; + homebrewStoreButton_ = nullptr; } if (!lastText_.empty() && gameButtons.empty()) { diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 93d21a2984..199c379187 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -26,7 +26,7 @@ class GameBrowser : public UI::LinearLayout { public: - GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle_, std::string lastText, std::string lastLink, int flags = 0, UI::LayoutParams *layoutParams = 0); + GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags = 0, UI::LayoutParams *layoutParams = nullptr); UI::Event OnChoice; UI::Event OnHoldChoice; @@ -37,6 +37,8 @@ public: void FocusGame(const std::string &gamePath); void SetPath(const std::string &path); + void Update() override; + protected: virtual bool DisplayTopBar(); virtual bool HasSpecialFiles(std::vector &filenames); @@ -57,15 +59,16 @@ private: UI::EventReturn HomeClick(UI::EventParams &e); UI::EventReturn PinToggleClick(UI::EventParams &e); - UI::ViewGroup *gameList_; + UI::ViewGroup *gameList_ = nullptr; PathBrowser path_; bool *gridStyle_; bool allowBrowsing_; std::string lastText_; std::string lastLink_; int flags_; - UI::Choice *homebrewStoreButton_; + UI::Choice *homebrewStoreButton_ = nullptr; std::string focusGamePath_; + bool listingPending_ = false; }; class RemoteISOBrowseScreen; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 8cf0a19ab9..f853e49e22 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -160,7 +160,9 @@ static bool LoadGameList(const std::string &host, int port, std::vector #include #include "base/stringutil.h" +#include "base/timeutil.h" #include "file/path.h" #include "net/http_client.h" #include "net/url.h" +#include "thread/threadutil.h" -bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector &files, const char *filter) { +bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector &files) { http::Client http; Buffer result; int code = 500; @@ -58,6 +60,28 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector ApplyFilter(std::vector files, const char *filter) { std::set filters; if (filter) { std::string tmp; @@ -73,40 +97,33 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector guard(pendingLock_); + pendingCancel_ = true; + pendingStop_ = true; + pendingCond_.notify_all(); + guard.unlock(); - files.push_back(info); + if (pendingThread_.joinable()) { + pendingThread_.join(); } - - - std::sort(files.begin(), files.end()); - return !files.empty(); } // Normalize slashes. void PathBrowser::SetPath(const std::string &path) { if (path[0] == '!') { path_ = path; + HandlePath(); return; } path_ = path; @@ -115,9 +132,78 @@ void PathBrowser::SetPath(const std::string &path) { } if (!path_.size() || (path_[path_.size() - 1] != '/')) path_ += "/"; + HandlePath(); +} + +void PathBrowser::HandlePath() { + std::lock_guard guard(pendingLock_); + + if (!path_.empty() && path_[0] == '!') { + ready_ = true; + pendingCancel_ = true; + pendingPath_.clear(); + return; + } + if (!startsWith(path_, "http://") && !startsWith(path_, "https://")) { + ready_ = true; + pendingCancel_ = true; + pendingPath_.clear(); + return; + } + + ready_ = false; + pendingCancel_ = false; + pendingFiles_.clear(); + pendingPath_ = path_; + pendingCond_.notify_all(); + + if (pendingThread_.joinable()) + return; + + pendingThread_ = std::thread([&] { + setCurrentThreadName("PathBrowser"); + + std::unique_lock guard(pendingLock_); + std::vector results; + std::string lastPath; + while (!pendingStop_) { + while (lastPath == pendingPath_ && !pendingCancel_) { + pendingCond_.wait(guard); + } + lastPath = pendingPath_; + bool success = false; + if (!lastPath.empty()) { + guard.unlock(); + results.clear(); + success = LoadRemoteFileList(lastPath, &pendingCancel_, results); + guard.lock(); + } + + if (pendingPath_ == lastPath) { + if (success && !pendingCancel_) { + pendingFiles_ = results; + } + pendingPath_.clear(); + lastPath.clear(); + ready_ = true; + } + } + }); +} + +bool PathBrowser::IsListingReady() { + return ready_; } bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter, bool *cancel) { + std::unique_lock guard(pendingLock_); + while (!IsListingReady() && (!cancel || !*cancel)) { + // In case cancel changes, just sleep. + guard.unlock(); + sleep_ms(100); + guard.lock(); + } + #ifdef _WIN32 if (path_ == "/") { // Special path that means root of file system. @@ -138,7 +224,8 @@ bool PathBrowser::GetListing(std::vector &fileInfo, const char *filter #endif if (startsWith(path_, "http://") || startsWith(path_, "https://")) { - return LoadRemoteFileList(path_, cancel, fileInfo, filter); + fileInfo = ApplyFilter(pendingFiles_, filter); + return true; } else { getFilesInDir(path_.c_str(), &fileInfo, filter); return true; @@ -167,4 +254,5 @@ void PathBrowser::Navigate(const std::string &path) { if (path_[path_.size() - 1] != '/') path_ += "/"; } + HandlePath(); } diff --git a/ext/native/file/path.h b/ext/native/file/path.h index fe4b78b776..ed14962156 100644 --- a/ext/native/file/path.h +++ b/ext/native/file/path.h @@ -1,7 +1,10 @@ #pragma once +#include +#include #include #include +#include #include #include @@ -14,8 +17,10 @@ class PathBrowser { public: PathBrowser() {} PathBrowser(std::string path) { SetPath(path); } + ~PathBrowser(); void SetPath(const std::string &path); + bool IsListingReady(); bool GetListing(std::vector &fileInfo, const char *filter = nullptr, bool *cancel = nullptr); void Navigate(const std::string &path); @@ -39,6 +44,17 @@ public: return str; } +private: + void HandlePath(); + std::string path_; + std::string pendingPath_; + std::vector pendingFiles_; + std::condition_variable pendingCond_; + std::mutex pendingLock_; + std::thread pendingThread_; + bool pendingCancel_ = false; + bool pendingStop_ = false; + bool ready_ = false; }; From d00eff7137c28286e1ebc3cca68ba7ab8b1ddf3f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 6 Oct 2019 12:40:00 -0700 Subject: [PATCH 31/33] UI: Allow pinning remote ISO paths. --- UI/MainScreen.cpp | 36 +++++++++++++++--------------------- UI/MainScreen.h | 26 +++++++++++++++++++++++--- UI/RemoteISOScreen.cpp | 36 +++++++++++++++++------------------- UI/RemoteISOScreen.h | 4 +++- ext/native/file/path.cpp | 2 ++ 5 files changed, 60 insertions(+), 44 deletions(-) diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 364e552f9d..bc2884a3c0 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -365,11 +365,6 @@ void GameButton::Draw(UIContext &dc) { dc.RebindTexture(); } -enum GameBrowserFlags { - FLAG_HOMEBREWSTOREBUTTON = 1 -}; - - class DirButton : public UI::Button { public: DirButton(const std::string &path, UI::LayoutParams *layoutParams) @@ -440,8 +435,8 @@ void DirButton::Draw(UIContext &dc) { } } -GameBrowser::GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags, UI::LayoutParams *layoutParams) - : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), allowBrowsing_(allowBrowsing), lastText_(lastText), lastLink_(lastLink), flags_(flags) { +GameBrowser::GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams) + : LinearLayout(UI::ORIENT_VERTICAL, layoutParams), path_(path), gridStyle_(gridStyle), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink) { using namespace UI; Refresh(); } @@ -530,7 +525,7 @@ void GameBrowser::Refresh() { // No topbar on recent screen if (DisplayTopBar()) { LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); - if (allowBrowsing_) { + if (browseFlags_ & BrowseFlags::NAVIGATE) { topBar->Add(new Spacer(2.0f)); topBar->Add(new TextView(path_.GetFriendlyPath().c_str(), ALIGN_VCENTER | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, 64.0f, 1.0f))); if (System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { @@ -585,7 +580,7 @@ void GameBrowser::Refresh() { isSaveData = true; if (!isGame && !isSaveData) { - if (allowBrowsing_) { + if (browseFlags_ & BrowseFlags::NAVIGATE) { dirButtons.push_back(new DirButton(fileInfo[i].fullName, fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT))); } } else { @@ -595,7 +590,7 @@ void GameBrowser::Refresh() { // Put RAR/ZIP files at the end to get them out of the way. They're only shown so that people // can click them and get an explanation that they need to unpack them. This is necessary due // to a flood of support email... - if (allowBrowsing_) { + if (browseFlags_ & BrowseFlags::ARCHIVES) { fileInfo.clear(); path_.GetListing(fileInfo, "zip:rar:r01:7z:"); if (!fileInfo.empty()) { @@ -613,7 +608,7 @@ void GameBrowser::Refresh() { } } - if (allowBrowsing_) { + if (browseFlags_ & BrowseFlags::NAVIGATE) { gameList_->Add(new DirButton("..", new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))-> OnClick.Handle(this, &GameBrowser::NavigateClick); @@ -645,7 +640,7 @@ void GameBrowser::Refresh() { } // Show a button to toggle pinning at the very end. - if (allowBrowsing_) { + if (browseFlags_ & BrowseFlags::PIN) { std::string caption = IsCurrentPathPinned() ? "-" : "+"; if (!*gridStyle_) { caption = IsCurrentPathPinned() ? mm->T("UnpinPath", "Unpin") : mm->T("PinPath", "Pin"); @@ -654,7 +649,7 @@ void GameBrowser::Refresh() { OnClick.Handle(this, &GameBrowser::PinToggleClick); } - if (flags_ & FLAG_HOMEBREWSTOREBUTTON) { + if (browseFlags_ & BrowseFlags::HOMEBREW_STORE) { Add(new Spacer()); homebrewStoreButton_ = Add(new Choice(mm->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT))); } else { @@ -801,7 +796,7 @@ void MainScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("MainScreenRecentGames"); GameBrowser *tabRecentGames = new GameBrowser( - "!RECENT", false, &g_Config.bGridView1, "", "", 0, + "!RECENT", BrowseFlags::NONE, &g_Config.bGridView1, "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); gameBrowsers_.push_back(tabRecentGames); @@ -819,12 +814,11 @@ void MainScreen::CreateViews() { ScrollView *scrollHomebrew = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollHomebrew->SetTag("MainScreenHomebrew"); - GameBrowser *tabAllGames = new GameBrowser(g_Config.currentDirectory, true, &g_Config.bGridView2, - mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", 0, + GameBrowser *tabAllGames = new GameBrowser(g_Config.currentDirectory, BrowseFlags::STANDARD, &g_Config.bGridView2, + mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); - GameBrowser *tabHomebrew = new GameBrowser(GetSysDirectory(DIRECTORY_GAME), false, &g_Config.bGridView3, + GameBrowser *tabHomebrew = new GameBrowser(GetSysDirectory(DIRECTORY_GAME), BrowseFlags::HOMEBREW_STORE, &g_Config.bGridView3, mm->T("How to get homebrew & demos", "How to get homebrew && demos"), "https://www.ppsspp.org/gethomebrew.html", - FLAG_HOMEBREWSTOREBUTTON, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); Choice *hbStore = tabHomebrew->HomebrewStoreButton(); @@ -1274,7 +1268,7 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("UmdReplaceRecentGames"); GameBrowser *tabRecentGames = new GameBrowser( - "!RECENT", false, &g_Config.bGridView1, "", "", 0, + "!RECENT", BrowseFlags::NONE, &g_Config.bGridView1, "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); leftColumn->AddTab(mm->T("Recent"), scrollRecentGames); @@ -1284,8 +1278,8 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollAllGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollAllGames->SetTag("UmdReplaceAllGames"); - GameBrowser *tabAllGames = new GameBrowser(g_Config.currentDirectory, true, &g_Config.bGridView2, - mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", 0, + GameBrowser *tabAllGames = new GameBrowser(g_Config.currentDirectory, BrowseFlags::STANDARD, &g_Config.bGridView2, + mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollAllGames->Add(tabAllGames); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 199c379187..eb15bd0f22 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -24,9 +24,30 @@ #include "ui/viewgroup.h" #include "UI/MiscScreens.h" +enum GameBrowserFlags { + FLAG_HOMEBREWSTOREBUTTON = 1 +}; + +enum class BrowseFlags { + NONE = 0, + NAVIGATE = 1, + ARCHIVES = 2, + PIN = 4, + HOMEBREW_STORE = 8, + STANDARD = 1 | 2 | 4, +}; + +static inline BrowseFlags operator |(const BrowseFlags &lhs, const BrowseFlags &rhs) { + return BrowseFlags((int)lhs | (int)rhs); +} + +static inline bool operator &(const BrowseFlags &lhs, const BrowseFlags &rhs) { + return ((int)lhs & (int)rhs) != 0; +} + class GameBrowser : public UI::LinearLayout { public: - GameBrowser(std::string path, bool allowBrowsing, bool *gridStyle, std::string lastText, std::string lastLink, int flags = 0, UI::LayoutParams *layoutParams = nullptr); + GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr); UI::Event OnChoice; UI::Event OnHoldChoice; @@ -62,10 +83,9 @@ private: UI::ViewGroup *gameList_ = nullptr; PathBrowser path_; bool *gridStyle_; - bool allowBrowsing_; + BrowseFlags browseFlags_; std::string lastText_; std::string lastLink_; - int flags_; UI::Choice *homebrewStoreButton_ = nullptr; std::string focusGamePath_; bool listingPending_ = false; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index f853e49e22..396d9bfa36 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -147,13 +147,8 @@ static bool FindServer(std::string &resultHost, int &resultPort) { return false; } -static bool LoadGameList(const std::string &host, int port, std::vector &games) { - std::string subdir = RemoteSubdir(); - - char temp[1024]; - snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s", host.c_str(), port, subdir.c_str()); - - PathBrowser browser(temp); +static bool LoadGameList(const std::string &url, std::vector &games) { + PathBrowser browser(url); std::vector files; browser.GetListing(files, "iso:cso:pbp:elf:prx:ppdmp:", &scanCancelled); if (scanCancelled) { @@ -165,12 +160,6 @@ static bool LoadGameList(const std::string &host, int port, std::vectorpush(new RemoteISOBrowseScreen(games_)); + screenManager()->push(new RemoteISOBrowseScreen(url_, games_)); break; } } @@ -373,19 +362,26 @@ ScanStatus RemoteISOConnectScreen::GetStatus() { } void RemoteISOConnectScreen::ExecuteLoad() { - bool result = LoadGameList(host_, port_, games_); + std::string subdir = RemoteSubdir(); + url_ = StringFromFormat("http://%s:%d%s", host_.c_str(), port_, subdir.c_str()); + bool result = LoadGameList(url_, games_); if (scanAborted) { return; } + if (result && !games_.empty() && !g_Config.bRemoteISOManual) { + g_Config.sLastRemoteISOServer = host_; + g_Config.iLastRemoteISOPort = port_; + } + std::lock_guard guard(statusLock_); status_ = result ? ScanStatus::LOADED : ScanStatus::FAILED; } class RemoteGameBrowser : public GameBrowser { public: - RemoteGameBrowser(const std::vector &games, bool allowBrowsing, bool *gridStyle_, std::string lastText, std::string lastLink, int flags = 0, UI::LayoutParams *layoutParams = 0) - : GameBrowser("!REMOTE", allowBrowsing, gridStyle_, lastText, lastLink, flags, layoutParams) { + RemoteGameBrowser(const std::string &url, const std::vector &games, BrowseFlags browseFlags, bool *gridStyle_, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr) + : GameBrowser(url, browseFlags, gridStyle_, lastText, lastLink, layoutParams) { games_ = games; Refresh(); } @@ -397,6 +393,7 @@ protected: bool HasSpecialFiles(std::vector &filenames) override; + std::string url_; std::vector games_; }; @@ -405,7 +402,8 @@ bool RemoteGameBrowser::HasSpecialFiles(std::vector &filenames) { return true; } -RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::vector &games) : games_(games) { +RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::string &url, const std::vector &games) + : url_(url), games_(games) { } void RemoteISOBrowseScreen::CreateViews() { @@ -426,7 +424,7 @@ void RemoteISOBrowseScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("RemoteGamesTab"); RemoteGameBrowser *tabRemoteGames = new RemoteGameBrowser( - games_, false, &g_Config.bGridView1, "", "", 0, + url_, games_, BrowseFlags::PIN, &g_Config.bGridView1, "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRemoteGames); gameBrowsers_.push_back(tabRemoteGames); diff --git a/UI/RemoteISOScreen.h b/UI/RemoteISOScreen.h index b7f7ff0f5c..06f7889a1c 100644 --- a/UI/RemoteISOScreen.h +++ b/UI/RemoteISOScreen.h @@ -72,16 +72,18 @@ protected: std::mutex statusLock_; std::string host_; int port_; + std::string url_; std::vector games_; }; class RemoteISOBrowseScreen : public MainScreen { public: - RemoteISOBrowseScreen(const std::vector &games); + RemoteISOBrowseScreen(const std::string &url, const std::vector &games); protected: void CreateViews() override; + std::string url_; std::vector games_; }; diff --git a/ext/native/file/path.cpp b/ext/native/file/path.cpp index d37eba5a76..ec62c179b5 100644 --- a/ext/native/file/path.cpp +++ b/ext/native/file/path.cpp @@ -66,6 +66,8 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector Date: Sun, 6 Oct 2019 16:11:15 -0700 Subject: [PATCH 32/33] Android: Setup UI callback earlier if possible. --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 55c5d9b192..5676afee23 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -838,8 +838,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C @Override public void onAttachedToWindow() { + Log.i(TAG, "onAttachedToWindow"); + super.onAttachedToWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - Log.i(TAG, "onAttachedToWindow"); DisplayCutout cutout = getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); if (cutout != null) { safeInsetLeft = cutout.getSafeInsetLeft(); @@ -855,6 +857,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C safeInsetBottom = 0; } } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + setupSystemUiCallback(); + } } @Override From e67a0606ef005434b7e2ad06fb1cb1bd8325a25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 7 Oct 2019 19:56:17 +0200 Subject: [PATCH 33/33] Vulkan: Always write gl_PointSize, fixes #12364. Remove unnecessary predeclaration of gl_Position. Replaces #12393. The predeclaration used to be required by early versions of glslang but is no longer. Since the GLSL compiler is controlled by us, we don't need to fear incompatibilities. --- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index 265bcbdd0a..696416201a 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -209,7 +209,6 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); } - WRITE(p, "out gl_PerVertex { vec4 gl_Position; };\n"); if (doBezier || doSpline) { WRITE(p, "struct TessData {\n"); @@ -635,6 +634,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, " }\n"); } WRITE(p, " gl_Position = outPos;\n"); + WRITE(p, " gl_PointSize = 1.0;\n"); if (gstate_c.Supports(GPU_NEEDS_Z_EQUAL_W_HACK)) { // See comment in GPU_Vulkan.cpp.