diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bba36f1b2..4bb16b8e50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -500,8 +500,6 @@ add_library(native STATIC native/base/stats.h native/base/stringutil.cpp native/base/stringutil.h - native/base/threadutil.cpp - native/base/threadutil.h native/base/timeutil.cpp native/base/timeutil.h native/file/chunk_file.cpp @@ -573,6 +571,9 @@ add_library(native STATIC native/net/resolve.h native/profiler/profiler.cpp native/profiler/profiler.h + native/thread/prioritizedworkqueue.cpp + native/thread/threadutil.cpp + native/thread/threadutil.h native/ui/screen.cpp native/ui/screen.h native/ui/ui.cpp diff --git a/Common/ChunkFile.h b/Common/ChunkFile.h index 9edc88a850..083d22f0ac 100644 --- a/Common/ChunkFile.h +++ b/Common/ChunkFile.h @@ -47,7 +47,7 @@ #if defined(ANDROID) || defined(IOS) || (defined(__APPLE__) && !defined(__MAC_10_7)) namespace std { - using tr1::is_pointer; + //using tr1::is_pointer; } #endif #ifdef __SYMBIAN32__ diff --git a/Core/Config.cpp b/Core/Config.cpp index 3273541853..f266d49bf7 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "Common/FileUtil.h" #include "Config.h" #include "IniFile.h" #include "HLE/sceUtility.h" @@ -122,6 +123,7 @@ void Config::Load(const char *iniFileName) pspConfig->Get("TimeFormat", &itimeformat, PSP_SYSTEMPARAM_TIME_FORMAT_24HR); pspConfig->Get("EncryptSave", &bEncryptSave, true); + CleanRecent(); // Ephemeral settings bDrawWireframe = false; } @@ -129,6 +131,7 @@ void Config::Load(const char *iniFileName) void Config::Save() { if (iniFilename_.size() && g_Config.bSaveSettings) { + CleanRecent(); IniFile iniFile; if (!iniFile.Load(iniFilename_.c_str())) { ERROR_LOG(LOADER, "Error saving config - can't read ini %s", iniFilename_.c_str()); @@ -210,3 +213,13 @@ void Config::AddRecent(const std::string &file) { if (recentIsos.size() > MAX_RECENT) recentIsos.resize(MAX_RECENT); } + +void Config::CleanRecent() { + std::vector cleanedRecent; + for (size_t i = 0; i < recentIsos.size(); i++) { + if (File::Exists(recentIsos[i])) + cleanedRecent.push_back(recentIsos[i]); + } + recentIsos = cleanedRecent; + +} \ No newline at end of file diff --git a/Core/Config.h b/Core/Config.h index cd79f969e5..c2ec1a53a1 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -98,6 +98,7 @@ public: // Utility functions for "recent" management void AddRecent(const std::string &file); + void CleanRecent(); private: std::string iniFilename_; diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 280a0cac18..e9cd741770 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -35,6 +35,8 @@ // vi2uc.q // sv.s +// TODO: Test and maybe fix: https://code.google.com/p/jpcsp/source/detail?r=3082# + #include "../Core.h" #include "Core/Reporting.h" @@ -92,7 +94,7 @@ using std::isinf; #define isinf(x) (!_finite(x) && !_isnan(x)) #endif #ifdef ANDROID -using __captured::isinf; +using std::isinf; #endif // Preserves NaN in first param, takes sign of equal second param. diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index cf5814138c..f9b18a9a3d 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -340,6 +340,9 @@ void Jit::Comp_Generic(u32 op) void Jit::WriteExit(u32 destination, int exit_num) { + if (!Memory::IsValidAddress(destination)) { + ERROR_LOG(JIT, "Trying to write block exit to illegal destination %08x: pc = %08x", destination, currentMIPS->pc); + } // If we need to verify coreState and rewind, we may not jump yet. if (js.afterOp & (JitState::AFTER_CORE_STATE | JitState::AFTER_REWIND_PC_BAD_STATE)) { diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index 99e739abf8..ca51e88c39 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -751,8 +751,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { break; case GE_CMD_TEXENVCOLOR: - if (diff) - shaderManager_->DirtyUniform(DIRTY_TEXENV); + shaderManager_->DirtyUniform(DIRTY_TEXENV); break; case GE_CMD_TEXFUNC: diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index eb5a1b1d91..40f1bd92f4 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -43,6 +43,7 @@ #include "UI/MenuScreens.h" #include "UI/EmuScreen.h" +#include "UI/GameInfoCache.h" EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) { CheckGLExtensions(); @@ -91,6 +92,8 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) { LayoutGamepad(dp_xres, dp_yres); + g_gameInfoCache.FlushBGs(); + NOTICE_LOG(BOOT, "Loading %s...", fileToStart.c_str()); } diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 9b3437d43d..7f85258c91 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -21,6 +21,7 @@ #include "base/timeutil.h" #include "base/stringutil.h" #include "image/png_load.h" +#include "thread/prioritizedworkqueue.h" #include "GameInfoCache.h" #include "Core/FileSystems/ISOFileSystem.h" #include "Core/FileSystems/DirectoryFileSystem.h" @@ -28,15 +29,8 @@ GameInfoCache g_gameInfoCache; -GameInfoCache::~GameInfoCache() -{ - for (auto iter = info_.begin(); iter != info_.end(); iter++) { - delete iter->second; - } -} -static bool ReadFileToString(IFileSystem *fs, const char *filename, std::string *contents) -{ +static bool ReadFileToString(IFileSystem *fs, const char *filename, std::string *contents) { PSPFileInfo info = fs->GetFileInfo(filename); if (!info.exists) { contents->clear(); @@ -55,6 +49,111 @@ static bool ReadFileToString(IFileSystem *fs, const char *filename, std::string return true; } + +class GameInfoWorkItem : public PrioritizedWorkQueueItem { +public: + GameInfoWorkItem(const std::string &gamePath, GameInfo *info) + : gamePath_(gamePath), info_(info) { + } + + virtual void run() { + // A game can be either an UMD or a directory under ms0:/PSP/GAME . + if (startsWith(gamePath_, "ms0:/PSP/GAME")) { + return; + // TODO: The case of these extensions is not perfect. + } else if (endsWith(gamePath_, ".PBP")) { + PBPReader pbp(gamePath_.c_str()); + if (!pbp.IsValid()) + return; + + // First, PARAM.SFO. + size_t sfoSize; + u8 *sfoData = pbp.GetSubFile(PBP_PARAM_SFO, &sfoSize); + { + lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO(sfoData, sfoSize); + info_->title = info_->paramSFO.GetValueString("TITLE"); + } + delete [] sfoData; + + // Then, ICON0.PNG. + { + lock_guard lock(info_->lock); + if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) { + pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->iconTextureData); + } else { + // We should load a default image here. + } + } + + if (info_->wantBG) { + { + lock_guard lock(info_->lock); + if (pbp.GetSubFileSize(PBP_PIC1_PNG) > 0) + pbp.GetSubFileAsString(PBP_PIC1_PNG, &info_->pic1TextureData); + } + } + } else if (endsWith(gamePath_, ".elf") || endsWith(gamePath_, ".prx")) { + return; + } else { + SequentialHandleAllocator handles; + // Let's assume it's an ISO. + // TODO: This will currently read in the whole directory tree. Not really necessary for just a + // few files. + BlockDevice *bd = constructBlockDevice(gamePath_.c_str()); + if (!bd) + return; // nothing to do here.. + ISOFileSystem umd(&handles, bd, "/PSP_GAME"); + + // Alright, let's fetch the PARAM.SFO. + std::string paramSFOcontents; + if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents)) { + lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); + info_->title = info_->paramSFO.GetValueString("TITLE"); + } + + ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->iconTextureData); + if (info_->wantBG) { + { + lock_guard lock(info_->lock); + ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData); + } + { + lock_guard lock(info_->lock); + ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData); + } + } + } + } + + virtual float priority() { + return info_->lastAccessedTime; + } + +private: + std::string gamePath_; + GameInfo *info_; + DISALLOW_COPY_AND_ASSIGN(GameInfoWorkItem); +}; + + + +GameInfoCache::~GameInfoCache() { + for (auto iter = info_.begin(); iter != info_.end(); iter++) { + delete iter->second; + } +} + +void GameInfoCache::Init() { + gameInfoWQ_ = new PrioritizedWorkQueue(); + ProcessWorkQueueOnThreadWhile(gameInfoWQ_); +} + +void GameInfoCache::Shutdown() { + StopProcessingWorkQueue(gameInfoWQ_); +} + void GameInfoCache::Save() { // TODO @@ -88,6 +187,10 @@ void GameInfoCache::FlushBGs() { } } +void GameInfoCache::Add(const std::string &key, GameInfo *info_) { + +} + // This may run off-main-thread and we thus can't use the global // pspFileSystem (well, we could with synchronization but there might not // even be a game running). @@ -129,89 +232,12 @@ GameInfo *GameInfoCache::GetInfo(const std::string &gamePath, bool wantBG) { again: - // return info; + GameInfo *info = new GameInfo(); + info->wantBG = wantBG; - // TODO: Everything below here should be asynchronous and run on a thread, - // filling in the info as it goes. + GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info); + gameInfoWQ_->Add(item); - // A game can be either an UMD or a directory under ms0:/PSP/GAME . - if (startsWith(gamePath, "ms0:/PSP/GAME")) { - return 0; - // TODO: The case of these extensions is not perfect. - } else if (endsWith(gamePath, ".PBP")) { - PBPReader pbp(gamePath.c_str()); - if (!pbp.IsValid()) - return 0; - GameInfo *info = new GameInfo(); - info->wantBG = wantBG; - - // First, PARAM.SFO. - size_t sfoSize; - u8 *sfoData = pbp.GetSubFile(PBP_PARAM_SFO, &sfoSize); - { - lock_guard lock(info->lock); - info->paramSFO.ReadSFO(sfoData, sfoSize); - info->title = info->paramSFO.GetValueString("TITLE"); - } - delete [] sfoData; - - // Then, ICON0.PNG. - { - lock_guard lock(info->lock); - if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) { - pbp.GetSubFileAsString(PBP_ICON0_PNG, &info->iconTextureData); - } else { - // We should load a default image here. - } - } - - if (info->wantBG) { - { - lock_guard lock(info->lock); - if (pbp.GetSubFileSize(PBP_PIC1_PNG) > 0) - pbp.GetSubFileAsString(PBP_PIC1_PNG, &info->pic1TextureData); - } - } - info_[gamePath] = info; - return info; - - } else if (endsWith(gamePath, ".elf") || endsWith(gamePath, ".prx")) { - return 0; - } else { - SequentialHandleAllocator handles; - // Let's assume it's an ISO. - // TODO: This will currently read in the whole directory tree. Not really necessary for just a - // few files. - BlockDevice *bd = constructBlockDevice(gamePath.c_str()); - if (!bd) - return 0; // nothing to do here.. - ISOFileSystem umd(&handles, bd, "/PSP_GAME"); - - GameInfo *info = new GameInfo(); - info->wantBG = wantBG; - - // Alright, let's fetch the PARAM.SFO. - std::string paramSFOcontents; - if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents)) { - lock_guard lock(info->lock); - info->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); - info->title = info->paramSFO.GetValueString("TITLE"); - } - - ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info->iconTextureData); - if (wantBG) { - { - lock_guard lock(info->lock); - ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info->pic0TextureData); - } - { - lock_guard lock(info->lock); - ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info->pic1TextureData); - } - } - info_[gamePath] = info; - return info; - } - - return 0; + info_[gamePath] = info; + return info; } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 4ba41953d6..ebbb9c0b67 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -21,10 +21,12 @@ #include #include "base/mutex.h" +#include "thread/prioritizedworkqueue.h" #include "gfx/texture.h" #include "Core/ELF/ParamSFO.h" -struct GameInfo { +class GameInfo { +public: GameInfo() : iconTexture(NULL), pic0Texture(NULL), pic1Texture(NULL) {} // Hold this when reading or writing from the GameInfo. // Don't need to hold it when just passing around the pointer, @@ -56,8 +58,13 @@ struct GameInfo { class GameInfoCache { public: + GameInfoCache() : gameInfoWQ_(0) {} ~GameInfoCache(); + // This creates a background worker thread! + void Init(); + void Shutdown(); + // All data in GameInfo including iconTexture may be zero the first time you call this // but filled in later asynchronously in the background. So keep calling this, // redrawing the UI often. Only set wantBG if you really want it because @@ -70,9 +77,14 @@ public: void Save(); void Load(); + void Add(const std::string &key, GameInfo *info_); + private: // Maps ISO path to info. std::map info_; + + // Work queue and management + PrioritizedWorkQueue *gameInfoWQ_; }; // This one can be global, no good reason not to. diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index bee8416c3a..a628ce40fb 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -214,8 +214,8 @@ void MenuScreen::render() { g_Config.Save(); screenManager()->switchScreen(new EmuScreen(fileName.toStdString())); } -#elif _WIN32 - MainWindow::BrowseAndBoot(""); +//#elif _WIN32 +// MainWindow::BrowseAndBoot(""); #else FileSelectScreenOptions options; options.allowChooseDirectory = true; @@ -268,9 +268,14 @@ void MenuScreen::render() { // This might create a texture so we must flush first. UIFlush(); GameInfo *ginfo = g_gameInfoCache.GetInfo(g_Config.recentIsos[i], false); - if (ginfo) { - if (UITextureButton(ctx, (int)GEN_ID_LOOP(i), vgrid_recent, 144, 80, ginfo->iconTexture, ALIGN_LEFT)) { + u32 color; + if (ginfo->iconTexture == 0) { + color = 0; + } else { + color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2)); + } + if (UITextureButton(ctx, (int)GEN_ID_LOOP(i), vgrid_recent, 144, 80, ginfo->iconTexture, ALIGN_LEFT, color)) { screenManager()->switchScreen(new EmuScreen(g_Config.recentIsos[i])); } } else { @@ -527,23 +532,31 @@ void FileListAdapter::drawItem(int item, int x, int y, int w, int h, bool select icon = iter->second; } - int iconSpace = this->itemHeight(item); + int iconSpace = 144; ui_draw2d.DrawImage2GridH(selected ? I_BUTTON_SELECTED: I_BUTTON, x, y, x + w); ui_draw2d.DrawTextShadow(UBUNTU24, (*items_)[item].name.c_str(), x + UI_SPACE + iconSpace, y + 25, 0xFFFFFFFF, ALIGN_LEFT | ALIGN_VCENTER); -#if 0 +#if 1 // This might create a texture so we must flush first. UIFlush(); GameInfo *ginfo = 0; - if (!(*items_)[item].isDirectory) + if (!(*items_)[item].isDirectory) { ginfo = g_gameInfoCache.GetInfo((*items_)[item].fullName, false); - if (ginfo && ginfo->iconTexture) { - float scaled_w = h * (144.f / 80.f); - UIFlush(); - ginfo->iconTexture->Bind(0); - ui_draw2d.DrawTexRect(x + 10, y, x + 10 + scaled_w, y + h, 0, 0, 1, 1, 0xFFFFFFFF); - ui_draw2d.Flush(); - ctx_->RebindTexture(); + if (!ginfo) { + ELOG("No ginfo :( %s", (*items_)[item].fullName.c_str()); + } + } + if (ginfo) { + float scaled_h = ui_atlas.images[I_BUTTON].h; + float scaled_w = scaled_h * (144.f / 80.f); + if (ginfo->iconTexture) { + uint32_t color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2)); + UIFlush(); + ginfo->iconTexture->Bind(0); + ui_draw2d.DrawTexRect(x + 10, y, x + 10 + scaled_w, y + scaled_h, 0, 0, 1, 1, color); + ui_draw2d.Flush(); + ctx_->RebindTexture(); + } } else { if (icon != -1) ui_draw2d.DrawImage(icon, x + UI_SPACE, y + 25, 1.0f, 0xFFFFFFFF, ALIGN_VCENTER | ALIGN_LEFT); @@ -640,6 +653,7 @@ static const char * credits[] = { "tpunix", "orphis", "sum2012", + "mikusp", "artart78", "tmaul", "ced2911", diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c0d0b336c6..6d03c0971c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -23,6 +23,12 @@ // // Windows has its own code that bypasses the framework entirely. + +// Background worker threads should be spawned in NativeInit and joined +// in NativeShutdown. + + + #include "base/logging.h" #include "base/NativeApp.h" #include "file/vfs.h" @@ -51,6 +57,7 @@ #include "ui_atlas.h" #include "EmuScreen.h" #include "MenuScreens.h" +#include "GameInfoCache.h" #include "UIShader.h" #ifdef ARM @@ -73,6 +80,7 @@ static std::string pendingMessage; static std::string pendingValue; static UIContext *uiContext; +std::thread *graphicsLoadThread; class AndroidLogger : public LogListener { @@ -322,6 +330,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co if (!boot_filename.empty() && stateToLoad != NULL) SaveState::Load(stateToLoad); + + g_gameInfoCache.Init(); } void NativeInitGraphics() @@ -466,6 +476,8 @@ void NativeShutdownGraphics() void NativeShutdown() { + g_gameInfoCache.Shutdown(); + delete host; host = 0; g_Config.Save(); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index f10fab8b96..f66e7f9a10 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -2,7 +2,6 @@ #include "base/display.h" #include "base/timeutil.h" -#include "base/threadutil.h" #include "base/NativeApp.h" #include "Log.h" #include "StringUtil.h" @@ -14,6 +13,7 @@ #include "../Core/Host.h" #include "../Core/System.h" #include "../Core/Config.h" +#include "thread/threadutil.h" #include #include diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 10ec4197ba..3c2c3f2443 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -7,7 +7,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := native_audio LOCAL_CFLAGS := -O2 -fsigned-char -ffast-math -Wall -Wno-multichar -Wno-psabi # yes, it's really CPPFLAGS for C++ -LOCAL_CPPFLAGS := -std=gnu++0x +LOCAL_CPPFLAGS := -std=gnu++11 -frtti NATIVE := ../../native LOCAL_SRC_FILES := \ $(NATIVE)/android/native-audio-so.cpp @@ -28,7 +28,7 @@ SRC := ../.. LOCAL_CFLAGS := -DUSE_PROFILER -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -O2 -fsigned-char -Wall -Wno-multichar -Wno-psabi -Wno-unused-variable -fno-strict-aliasing -ffast-math # yes, it's really CPPFLAGS for C++ -LOCAL_CPPFLAGS := -std=gnu++0x +LOCAL_CPPFLAGS := -std=gnu++11 -frtti LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../../Common \ $(LOCAL_PATH)/../.. \ diff --git a/android/jni/Application.mk b/android/jni/Application.mk index e2988a7571..fa12ba8a99 100644 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -1,4 +1,4 @@ -APP_STL := stlport_static +APP_STL := gnustl_static APP_ABI := armeabi-v7a armeabi x86 - #APP_ABI := armeabi-v7a +APP_GNUSTL_CPP_FEATURES := rtti \ No newline at end of file diff --git a/native b/native index bff1115f0c..25711066df 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit bff1115f0ccd861ee016e5d4027c25af8bffb5c2 +Subproject commit 25711066df80e789515c65ef05b05d1290e2191a