mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 02:29:51 +01:00
Merge branch 'master' into TemporaryHackishBranch
This commit is contained in:
commit
ccdad3cdd2
59 files changed
+901
-290
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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] {};
|
||||
|
||||
+3
-2
@@ -1160,8 +1160,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> guard(blocksMutex_);
|
||||
@@ -278,7 +282,6 @@ void CachingFileLoader::StartReadAhead(s64 pos) {
|
||||
}
|
||||
}
|
||||
|
||||
aheadThread_ = false;
|
||||
aheadThreadRunning_ = false;
|
||||
});
|
||||
th.detach();
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/Loaders.h"
|
||||
@@ -75,6 +76,7 @@ private:
|
||||
|
||||
std::map<s64, BlockInfo> blocks_;
|
||||
std::recursive_mutex blocksMutex_;
|
||||
bool aheadThread_ = false;
|
||||
bool aheadThreadRunning_ = false;
|
||||
std::thread aheadThread_;
|
||||
std::once_flag preparedFlag_;
|
||||
};
|
||||
@@ -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<std::mutex> guard(blocksMutex_);
|
||||
blocks_.clear();
|
||||
@@ -118,7 +120,7 @@ void RamCachingFileLoader::ShutdownCache() {
|
||||
}
|
||||
|
||||
void RamCachingFileLoader::Cancel() {
|
||||
if (aheadThread_) {
|
||||
if (aheadThreadRunning_) {
|
||||
std::lock_guard<std::mutex> guard(blocksMutex_);
|
||||
aheadCancel_ = true;
|
||||
}
|
||||
@@ -213,14 +215,16 @@ void RamCachingFileLoader::StartReadAhead(s64 pos) {
|
||||
|
||||
std::lock_guard<std::mutex> 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() {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#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;
|
||||
};
|
||||
@@ -35,8 +35,10 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
#if !PPSSPP_PLATFORM(SWITCH)
|
||||
#include <dlfcn.h>
|
||||
#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
|
||||
|
||||
+3
-3
@@ -654,7 +654,6 @@ void __IoInit() {
|
||||
if (ioManagerThreadEnabled) {
|
||||
Core_ListenLifecycle(&__IoWakeManager);
|
||||
ioManagerThread = new std::thread(&__IoManagerThread);
|
||||
ioManagerThread->detach();
|
||||
}
|
||||
|
||||
__KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback);
|
||||
@@ -731,9 +730,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/Debugger/DebugInterface.h"
|
||||
|
||||
+18
-6
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
+63
-8
@@ -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 <deque>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
@@ -70,6 +71,13 @@ namespace Reporting
|
||||
// The latest compatibility result from the server.
|
||||
static std::vector<std::string> lastCompatResult;
|
||||
|
||||
static std::mutex pendingMessageLock;
|
||||
static std::condition_variable pendingMessageCond;
|
||||
static std::deque<int> 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<std::string, u32> 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;
|
||||
}
|
||||
|
||||
@@ -269,6 +279,8 @@ namespace Reporting
|
||||
return "Mac";
|
||||
#elif defined(LOONGSON)
|
||||
return "Loongson";
|
||||
#elif defined(__SWITCH__)
|
||||
return "Switch";
|
||||
#elif defined(__linux__)
|
||||
return "Linux";
|
||||
#elif defined(__Bitrig__)
|
||||
@@ -295,10 +307,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 +563,28 @@ namespace Reporting
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ProcessPending() {
|
||||
setCurrentThreadName("Report");
|
||||
|
||||
std::unique_lock<std::mutex> 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 +607,13 @@ namespace Reporting
|
||||
payload.string1 = message;
|
||||
payload.string2 = temp;
|
||||
|
||||
std::thread th(Process, pos);
|
||||
th.detach();
|
||||
std::lock_guard<std::mutex> 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 +629,13 @@ namespace Reporting
|
||||
payload.string1 = message;
|
||||
payload.string2 = formatted;
|
||||
|
||||
std::thread th(Process, pos);
|
||||
th.detach();
|
||||
std::lock_guard<std::mutex> 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 +654,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<std::string> CompatibilitySuggestions() {
|
||||
|
||||
+7
-2
@@ -153,11 +153,12 @@ namespace SaveState
|
||||
|
||||
void ScheduleCompress(std::vector<u8> *result, const std::vector<u8> *state, const std::vector<u8> *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<u8> &result, const std::vector<u8> &state, const std::vector<u8> &base)
|
||||
@@ -208,6 +209,9 @@ namespace SaveState
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (compressThread_.joinable())
|
||||
compressThread_.join();
|
||||
|
||||
// This lock is mainly for shutdown.
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
first_ = 0;
|
||||
@@ -233,6 +237,7 @@ namespace SaveState
|
||||
StateBuffer bases_[2];
|
||||
std::vector<int> baseMapping_;
|
||||
std::mutex lock_;
|
||||
std::thread compressThread_;
|
||||
|
||||
int base_;
|
||||
int baseUsage_;
|
||||
|
||||
@@ -268,6 +268,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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -86,8 +86,9 @@ private:
|
||||
std::string GetISOGameID(FileLoader *loader) const;
|
||||
std::shared_ptr<http::Download> curDownload_;
|
||||
std::shared_ptr<std::thread> installThread_;
|
||||
bool installInProgress_;
|
||||
float installProgress_;
|
||||
bool installInProgress_ = false;
|
||||
bool installDonePending_ = false;
|
||||
float installProgress_ = 0.0f;
|
||||
std::string installError_;
|
||||
};
|
||||
|
||||
|
||||
+6
-1
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -210,7 +210,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");
|
||||
@@ -638,6 +637,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.
|
||||
|
||||
@@ -14,7 +14,12 @@ No BIOS file required to play, PPSSPP is an "HLE" emulator. Default settings ba
|
||||
Includes adhoc between multiply instances of PPSSPP on same PC with some additional options to make that easier.
|
||||
~ LunaMoo
|
||||
|
||||
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)
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
@@ -570,3 +571,126 @@ void AnalogTestScreen::CreateViews() {
|
||||
|
||||
root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(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<UIScreen>(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();
|
||||
ui_context->BeginNoTex();
|
||||
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);
|
||||
}
|
||||
}
|
||||
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),
|
||||
#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,
|
||||
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();
|
||||
|
||||
UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) {
|
||||
System_SendMessage("immersive", "");
|
||||
if (g_Config.iAndroidHwScale != 0) {
|
||||
RecreateActivity();
|
||||
}
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
@@ -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;
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
enum {
|
||||
MAX_TOUCH_POINTS = 10,
|
||||
};
|
||||
TrackedTouch touches_[MAX_TOUCH_POINTS]{};
|
||||
|
||||
virtual void CreateViews() override;
|
||||
UI::EventReturn OnImmersiveModeChange(UI::EventParams &e);
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -899,7 +899,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");
|
||||
@@ -1408,6 +1408,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);
|
||||
@@ -1573,6 +1574,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;
|
||||
|
||||
@@ -174,6 +174,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;
|
||||
|
||||
+37
-30
@@ -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), gameList_(0), 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();
|
||||
}
|
||||
@@ -510,10 +505,17 @@ bool GameBrowser::HasSpecialFiles(std::vector<std::string> &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();
|
||||
|
||||
@@ -523,14 +525,14 @@ void GameBrowser::Refresh() {
|
||||
// No topbar with SimpleUI
|
||||
if (!g_Config.bSimpleUI) { //(DisplayTopBar()) { // No topbar on recent screen
|
||||
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 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)));
|
||||
}
|
||||
@@ -556,12 +558,14 @@ void GameBrowser::Refresh() {
|
||||
std::vector<DirButton *> dirButtons;
|
||||
std::vector<GameButton *> gameButtons;
|
||||
|
||||
listingPending_ = !path_.IsListingReady();
|
||||
|
||||
std::vector<std::string> 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> fileInfo;
|
||||
path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:ppdmp:");
|
||||
for (size_t i = 0; i < fileInfo.size(); i++) {
|
||||
@@ -576,8 +580,8 @@ void GameBrowser::Refresh() {
|
||||
isSaveData = true;
|
||||
|
||||
if (!isGame && !isSaveData) {
|
||||
if (allowBrowsing_) {
|
||||
dirButtons.push_back(new DirButton(fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)));
|
||||
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 {
|
||||
gameButtons.push_back(new GameButton(fileInfo[i].fullName, *gridStyle_, new UI::LinearLayoutParams(*gridStyle_ == true ? UI::WRAP_CONTENT : UI::FILL_PARENT, UI::WRAP_CONTENT)));
|
||||
@@ -586,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()) {
|
||||
@@ -604,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);
|
||||
|
||||
@@ -616,6 +620,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);
|
||||
}
|
||||
@@ -632,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");
|
||||
@@ -641,11 +649,11 @@ 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 {
|
||||
homebrewStoreButton_ = 0;
|
||||
homebrewStoreButton_ = nullptr;
|
||||
}
|
||||
|
||||
if (!lastText_.empty() && gameButtons.empty()) {
|
||||
@@ -788,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);
|
||||
@@ -806,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();
|
||||
@@ -1257,7 +1264,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);
|
||||
@@ -1267,8 +1274,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);
|
||||
|
||||
+28
-5
@@ -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 = 0);
|
||||
GameBrowser(std::string path, BrowseFlags browseFlags, bool *gridStyle, std::string lastText, std::string lastLink, UI::LayoutParams *layoutParams = nullptr);
|
||||
|
||||
UI::Event OnChoice;
|
||||
UI::Event OnHoldChoice;
|
||||
@@ -37,6 +58,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<std::string> &filenames);
|
||||
@@ -57,15 +80,15 @@ 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_;
|
||||
BrowseFlags browseFlags_;
|
||||
std::string lastText_;
|
||||
std::string lastLink_;
|
||||
int flags_;
|
||||
UI::Choice *homebrewStoreButton_;
|
||||
UI::Choice *homebrewStoreButton_ = nullptr;
|
||||
std::string focusGamePath_;
|
||||
bool listingPending_ = false;
|
||||
};
|
||||
|
||||
class RemoteISOBrowseScreen;
|
||||
|
||||
+16
-3
@@ -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)
|
||||
@@ -869,11 +879,12 @@ void NativeShutdownGraphics() {
|
||||
winAudioBackend = nullptr;
|
||||
#endif
|
||||
|
||||
ShutdownWebServer();
|
||||
UIBackgroundShutdown();
|
||||
|
||||
delete g_gameInfoCache;
|
||||
g_gameInfoCache = nullptr;
|
||||
|
||||
UIBackgroundShutdown();
|
||||
|
||||
delete uiContext;
|
||||
uiContext = nullptr;
|
||||
|
||||
@@ -1006,6 +1017,7 @@ void NativeRender(GraphicsContext *graphicsContext) {
|
||||
}
|
||||
|
||||
if (resized) {
|
||||
ILOG("Resized flag set - recalculating bounds");
|
||||
resized = false;
|
||||
|
||||
if (uiContext) {
|
||||
@@ -1281,6 +1293,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");
|
||||
|
||||
+33
-91
@@ -20,10 +20,12 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "base/timeutil.h"
|
||||
#include "file/path.h"
|
||||
#include "i18n/i18n.h"
|
||||
#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 +41,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 +138,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,82 +147,19 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool LoadGameList(const std::string &host, int port, std::vector<std::string> &games) {
|
||||
http::Client http;
|
||||
Buffer result;
|
||||
int code = 500;
|
||||
std::vector<std::string> responseHeaders;
|
||||
// TODO: Use relative url parsing (URL::Relative) instead.
|
||||
std::string subdir = RemoteSubdir();
|
||||
|
||||
// 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);
|
||||
http.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (code != 200 || scanCancelled) {
|
||||
static bool LoadGameList(const std::string &url, std::vector<std::string> &games) {
|
||||
PathBrowser browser(url);
|
||||
std::vector<FileInfo> files;
|
||||
browser.GetListing(files, "iso:cso:pbp:elf:prx:ppdmp:", &scanCancelled);
|
||||
if (scanCancelled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string listing;
|
||||
std::vector<std::string> 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);
|
||||
for (auto &file : files) {
|
||||
if (RemoteISOFileSupported(file.name)) {
|
||||
games.push_back(file.fullName);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
subdir.clear();
|
||||
} 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[0] != '/')
|
||||
item.insert(0, "/");
|
||||
if (item.back() == '\r')
|
||||
item.pop_back();
|
||||
|
||||
if (!RemoteISOFileSupported(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char temp[1024] = {};
|
||||
snprintf(temp, sizeof(temp) - 1, "http://%s:%d%s%s", host.c_str(), port, subdir.c_str(), item.c_str());
|
||||
games.push_back(temp);
|
||||
}
|
||||
|
||||
// Save for next time unless manual is true
|
||||
if (!games.empty() && !g_Config.bRemoteISOManual) {
|
||||
g_Config.sLastRemoteISOServer = host;
|
||||
g_Config.iLastRemoteISOPort = port;
|
||||
}
|
||||
|
||||
return !games.empty();
|
||||
}
|
||||
|
||||
@@ -327,7 +257,6 @@ RemoteISOConnectScreen::RemoteISOConnectScreen() : status_(ScanStatus::SCANNING)
|
||||
scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) {
|
||||
thiz->ExecuteScan();
|
||||
}, this);
|
||||
scanThread_->detach();
|
||||
}
|
||||
|
||||
RemoteISOConnectScreen::~RemoteISOConnectScreen() {
|
||||
@@ -341,6 +270,8 @@ RemoteISOConnectScreen::~RemoteISOConnectScreen() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (scanThread_->joinable())
|
||||
scanThread_->join();
|
||||
delete scanThread_;
|
||||
}
|
||||
|
||||
@@ -384,11 +315,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,17 +333,18 @@ 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;
|
||||
|
||||
case ScanStatus::LOADED:
|
||||
TriggerFinish(DR_OK);
|
||||
screenManager()->push(new RemoteISOBrowseScreen(games_));
|
||||
screenManager()->push(new RemoteISOBrowseScreen(url_, games_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -432,19 +365,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<std::mutex> guard(statusLock_);
|
||||
status_ = result ? ScanStatus::LOADED : ScanStatus::FAILED;
|
||||
}
|
||||
|
||||
class RemoteGameBrowser : public GameBrowser {
|
||||
public:
|
||||
RemoteGameBrowser(const std::vector<std::string> &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<std::string> &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();
|
||||
}
|
||||
@@ -456,6 +396,7 @@ protected:
|
||||
|
||||
bool HasSpecialFiles(std::vector<std::string> &filenames) override;
|
||||
|
||||
std::string url_;
|
||||
std::vector<std::string> games_;
|
||||
};
|
||||
|
||||
@@ -464,7 +405,8 @@ bool RemoteGameBrowser::HasSpecialFiles(std::vector<std::string> &filenames) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::vector<std::string> &games) : games_(games) {
|
||||
RemoteISOBrowseScreen::RemoteISOBrowseScreen(const std::string &url, const std::vector<std::string> &games)
|
||||
: url_(url), games_(games) {
|
||||
}
|
||||
|
||||
void RemoteISOBrowseScreen::CreateViews() {
|
||||
@@ -485,7 +427,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);
|
||||
@@ -592,7 +534,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;
|
||||
|
||||
@@ -72,16 +72,18 @@ protected:
|
||||
std::mutex statusLock_;
|
||||
std::string host_;
|
||||
int port_;
|
||||
std::string url_;
|
||||
std::vector<std::string> games_;
|
||||
};
|
||||
|
||||
class RemoteISOBrowseScreen : public MainScreen {
|
||||
public:
|
||||
RemoteISOBrowseScreen(const std::vector<std::string> &games);
|
||||
RemoteISOBrowseScreen(const std::string &url, const std::vector<std::string> &games);
|
||||
|
||||
protected:
|
||||
void CreateViews() override;
|
||||
|
||||
std::string url_;
|
||||
std::vector<std::string> games_;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
@@ -849,6 +849,7 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\android\jni\TestRunner.h" />
|
||||
<ClInclude Include="..\ios\ViewController.h" />
|
||||
<ClInclude Include="..\ppsspp_config.h" />
|
||||
<ClInclude Include="..\Qt\mainwindow.h" />
|
||||
<ClInclude Include="..\Qt\QtHost.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
|
||||
@@ -353,6 +353,7 @@
|
||||
<ClInclude Include="WASAPIStream.h">
|
||||
<Filter>Windows\System</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ppsspp_config.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="icon1.ico">
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.ppsspp.ppsspp"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="19020000"
|
||||
android:versionName="1.9.2.0">
|
||||
android:versionCode="19030000"
|
||||
android:versionName="1.9.3.0">
|
||||
<!-- Note that versionCode should be in the format xyzzrrrr. Example: 16030000 -->
|
||||
<!-- In this same case, versionName should be 1.6.3.0 -->
|
||||
<!-- Also note that we are overriding these values anyway from gradle. -->
|
||||
@@ -40,7 +40,7 @@
|
||||
android:logo="@drawable/ic_banner"
|
||||
android:isGame="true"
|
||||
android:banner="@drawable/tv_banner">
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||
<meta-data android:name="android.max_aspect" android:value="2.4" />
|
||||
<activity
|
||||
android:name=".PpssppActivity"
|
||||
android:configChanges="locale|keyboard|keyboardHidden|navigation|uiMode"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+33
-23
@@ -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
|
||||
@@ -611,6 +612,31 @@ 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: 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);
|
||||
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 +648,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 +904,9 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JN
|
||||
display_dpi_x = dpi;
|
||||
display_dpi_y = dpi;
|
||||
display_hz = refreshRate;
|
||||
|
||||
recalculateDpi();
|
||||
NativeResized();
|
||||
}
|
||||
|
||||
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<style name="ppsspp_style">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:navigationBarColor">@color/black</item>
|
||||
<!-- TODO: Our rendering is not ready for these yet.
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item> -->
|
||||
</style>
|
||||
</resources>
|
||||
@@ -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;
|
||||
@@ -85,6 +86,9 @@ 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;
|
||||
private AudioManager audioManager;
|
||||
@@ -116,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,
|
||||
};
|
||||
@@ -404,17 +413,24 @@ 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()) {
|
||||
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 | 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");
|
||||
Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring for now");
|
||||
}
|
||||
updateDisplayMeasurements();
|
||||
}
|
||||
@@ -452,21 +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 (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 && 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();
|
||||
|
||||
@@ -539,9 +567,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);
|
||||
@@ -578,6 +603,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.
|
||||
@@ -601,7 +627,9 @@ 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);
|
||||
updateDisplayMeasurements();
|
||||
mSurface = holder.getSurface();
|
||||
if (!javaGL) {
|
||||
// If we got a surface, this starts the thread. If not, it doesn't.
|
||||
@@ -667,14 +695,26 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
void setupSystemUiCallback() {
|
||||
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
|
||||
final View decorView = getWindow().peekDecorView();
|
||||
if (decorView == null || decorView == navigationCallbackView) {
|
||||
return;
|
||||
}
|
||||
|
||||
decorView.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 & 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();
|
||||
}
|
||||
});
|
||||
navigationCallbackView = decorView;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -720,6 +760,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
|
||||
NativeApp.shutdown();
|
||||
initialized = false;
|
||||
}
|
||||
navigationCallbackView = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -795,6 +836,33 @@ 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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
setupSystemUiCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
Log.i(TAG, "onConfigurationChanged");
|
||||
@@ -803,7 +871,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -126,19 +125,16 @@ 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) {
|
||||
mController.onResume();
|
||||
|
||||
// According to the docs, the Moga's state can be inconsistent here.
|
||||
// We should do a one time poll. TODO
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.i(TAG, "onDestroy");
|
||||
if (mController != null) {
|
||||
mController.exit();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -131,7 +132,6 @@ public class NativeSurfaceView extends SurfaceView implements SensorEventListene
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.i(TAG, "onDestroy");
|
||||
if (mController != null) {
|
||||
mController.exit();
|
||||
}
|
||||
|
||||
+1
-1
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#include "ppsspp_config.h"
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#if PPSSPP_PLATFORM(SWITCH)
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4996)
|
||||
#define strncasecmp _strnicmp
|
||||
#else
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
// Dumb wrapper around itoa, providing a buffer. Declare this on the stack.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "ppsspp_config.h"
|
||||
#include "file/fd_util.h"
|
||||
|
||||
#include <errno.h>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+201
-3
@@ -1,9 +1,131 @@
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#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<FileInfo> &files) {
|
||||
http::Client http;
|
||||
Buffer result;
|
||||
int code = 500;
|
||||
std::vector<std::string> 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<std::string> 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;
|
||||
}
|
||||
|
||||
for (std::string item : items) {
|
||||
// Apply some workarounds.
|
||||
if (item.empty())
|
||||
continue;
|
||||
if (item.back() == '\r')
|
||||
item.pop_back();
|
||||
if (item == baseURL.Resource())
|
||||
continue;
|
||||
|
||||
FileInfo info;
|
||||
info.name = item;
|
||||
info.fullName = baseURL.Relative(item).ToString();
|
||||
info.isDirectory = endsWith(item, "/");
|
||||
info.exists = true;
|
||||
info.size = 0;
|
||||
info.isWritable = false;
|
||||
|
||||
files.push_back(info);
|
||||
}
|
||||
|
||||
return !files.empty();
|
||||
}
|
||||
|
||||
std::vector<FileInfo> ApplyFilter(std::vector<FileInfo> files, const char *filter) {
|
||||
std::set<std::string> 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));
|
||||
}
|
||||
|
||||
auto pred = [&](const FileInfo &info) {
|
||||
if (info.isDirectory || !filter)
|
||||
return false;
|
||||
std::string ext = getFileExtension(info.fullName);
|
||||
return filters.find(ext) == filters.end();
|
||||
};
|
||||
files.erase(std::remove_if(files.begin(), files.end(), pred), files.end());
|
||||
return files;
|
||||
}
|
||||
|
||||
PathBrowser::~PathBrowser() {
|
||||
std::unique_lock<std::mutex> guard(pendingLock_);
|
||||
pendingCancel_ = true;
|
||||
pendingStop_ = true;
|
||||
pendingCond_.notify_all();
|
||||
guard.unlock();
|
||||
|
||||
if (pendingThread_.joinable()) {
|
||||
pendingThread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize slashes.
|
||||
void PathBrowser::SetPath(const std::string &path) {
|
||||
if (path[0] == '!') {
|
||||
path_ = path;
|
||||
HandlePath();
|
||||
return;
|
||||
}
|
||||
path_ = path;
|
||||
@@ -12,9 +134,78 @@ void PathBrowser::SetPath(const std::string &path) {
|
||||
}
|
||||
if (!path_.size() || (path_[path_.size() - 1] != '/'))
|
||||
path_ += "/";
|
||||
HandlePath();
|
||||
}
|
||||
|
||||
void PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter) {
|
||||
void PathBrowser::HandlePath() {
|
||||
std::lock_guard<std::mutex> 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<std::mutex> guard(pendingLock_);
|
||||
std::vector<FileInfo> 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> &fileInfo, const char *filter, bool *cancel) {
|
||||
std::unique_lock<std::mutex> 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.
|
||||
@@ -34,7 +225,13 @@ void PathBrowser::GetListing(std::vector<FileInfo> &fileInfo, const char *filter
|
||||
}
|
||||
#endif
|
||||
|
||||
getFilesInDir(path_.c_str(), &fileInfo, filter);
|
||||
if (startsWith(path_, "http://") || startsWith(path_, "https://")) {
|
||||
fileInfo = ApplyFilter(pendingFiles_, filter);
|
||||
return true;
|
||||
} else {
|
||||
getFilesInDir(path_.c_str(), &fileInfo, filter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support paths like "../../hello"
|
||||
@@ -52,11 +249,12 @@ 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;
|
||||
if (path_[path_.size() - 1] != '/')
|
||||
path_ += "/";
|
||||
}
|
||||
HandlePath();
|
||||
}
|
||||
+17
-1
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -14,9 +17,11 @@ class PathBrowser {
|
||||
public:
|
||||
PathBrowser() {}
|
||||
PathBrowser(std::string path) { SetPath(path); }
|
||||
~PathBrowser();
|
||||
|
||||
void SetPath(const std::string &path);
|
||||
void GetListing(std::vector<FileInfo> &fileInfo, const char *filter = 0);
|
||||
bool IsListingReady();
|
||||
bool GetListing(std::vector<FileInfo> &fileInfo, const char *filter = nullptr, bool *cancel = nullptr);
|
||||
void Navigate(const std::string &path);
|
||||
|
||||
std::string GetPath() const {
|
||||
@@ -39,6 +44,17 @@ public:
|
||||
return str;
|
||||
}
|
||||
|
||||
private:
|
||||
void HandlePath();
|
||||
|
||||
std::string path_;
|
||||
std::string pendingPath_;
|
||||
std::vector<FileInfo> pendingFiles_;
|
||||
std::condition_variable pendingCond_;
|
||||
std::mutex pendingLock_;
|
||||
std::thread pendingThread_;
|
||||
bool pendingCancel_ = false;
|
||||
bool pendingStop_ = false;
|
||||
bool ready_ = false;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -428,16 +427,16 @@ int Client::ReadResponseEntity(Buffer *readbuf, const std::vector<std::string> &
|
||||
}
|
||||
|
||||
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<Download> 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 +576,7 @@ void Downloader::CancelAll() {
|
||||
for (size_t i = 0; i < downloads_.size(); i++) {
|
||||
downloads_[i]->Cancel();
|
||||
}
|
||||
downloads_.clear();
|
||||
}
|
||||
|
||||
} // http
|
||||
@@ -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<std::string> 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<void(Download &)> callback_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <map>
|
||||
|
||||
#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<VKRStep *> &steps, VkQueryPool queryPool, std::vector<std::string> *timestampDescriptions) {
|
||||
void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &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<VKRStep *> &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<VKRStep *> &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<VKRStep *> &steps) {
|
||||
|
||||
@@ -173,6 +173,14 @@ struct TransitionRequest {
|
||||
VkImageLayout targetLayout;
|
||||
};
|
||||
|
||||
struct QueueProfileContext {
|
||||
VkQueryPool queryPool;
|
||||
std::vector<std::string> 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<VKRStep *> &steps, VkQueryPool queryPool, std::vector<std::string> *timestampDescriptions);
|
||||
void RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &steps, QueueProfileContext *profile);
|
||||
void LogSteps(const std::vector<VKRStep *> &steps);
|
||||
|
||||
std::string StepToString(const VKRStep &step) const;
|
||||
|
||||
@@ -138,7 +138,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();
|
||||
@@ -230,7 +230,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());
|
||||
@@ -301,7 +301,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();
|
||||
}
|
||||
@@ -378,11 +378,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);
|
||||
@@ -393,19 +393,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)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,16 +419,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,7 +964,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));
|
||||
@@ -1064,7 +1065,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) {
|
||||
|
||||
@@ -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<std::string> timestampDescriptions;
|
||||
std::string profileSummary;
|
||||
};
|
||||
|
||||
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
|
||||
|
||||
@@ -125,6 +125,7 @@ void ScreenManager::deviceRestored() {
|
||||
}
|
||||
|
||||
void ScreenManager::resized() {
|
||||
ILOG("ScreenManager::resized(dp: %dx%d)", dp_xres, dp_yres);
|
||||
std::lock_guard<std::recursive_mutex> guard(inputLock_);
|
||||
// Have to notify the whole stack, otherwise there will be problems when going back
|
||||
// to non-top screens.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user