Merge branch 'master' into TemporaryHackishBranch

This commit is contained in:
LunaMoo committed 2019-10-07 22:31:24 +02:00
commit ccdad3cdd2
59 files changed
+901 -290

No files matched your search

+1 -1
View File
@@ -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;
+3
View File
@@ -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
View File
@@ -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));
}
}
+9 -6
View File
@@ -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();
}
+3 -1
View File
@@ -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_;
};
+10 -7
View File
@@ -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() {
+3 -1
View File
@@ -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;
};
+5 -1
View File
@@ -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
View File
@@ -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();
}
+4
View File
@@ -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
View File
@@ -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();
}
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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_;
+1
View File
@@ -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();
+12 -7
View File
@@ -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;
}
+3 -2
View File
@@ -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
View File
@@ -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();
}
+1
View File
@@ -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);
+1 -1
View File
@@ -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.
+6 -1
View File
@@ -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
View File
@@ -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;
+124
View File
@@ -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;
}
+28 -2
View File
@@ -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);
};
+1
View File
@@ -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
+7 -1
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3 -1
View File
@@ -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
View File
@@ -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.
+1
View File
@@ -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>
+1
View File
@@ -353,6 +353,7 @@
<ClInclude Include="WASAPIStream.h">
<Filter>Windows\System</Filter>
</ClInclude>
<ClInclude Include="..\ppsspp_config.h" />
</ItemGroup>
<ItemGroup>
<None Include="icon1.ico">
+3 -3
View File
@@ -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"
+2 -2
View File
@@ -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
View File
@@ -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() {
+13
View File
@@ -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
View File
@@ -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'
}
}
+5
View File
@@ -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>
+2
View File
@@ -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.
+8 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+5 -5
View File
@@ -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
+7 -6
View File
@@ -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_;
};
+6
View File
@@ -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);
+15 -6
View File
@@ -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;
}
+11 -4
View File
@@ -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) {
+9 -1
View File
@@ -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;
+20 -19
View File
@@ -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) {
+2 -4
View File
@@ -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];
+1
View File
@@ -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.
+3 -1
View File
@@ -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.
+2
View File
@@ -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