diff --git a/.gitmodules b/.gitmodules index 02c95724d6..a6bbcdb0c4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,9 @@ [submodule "assets/patches"] path = assets/patches url = https://github.com/LunaMoo/PPSSPP_workarounds.git +[submodule "ext/discord-rpc"] + path = ext/discord-rpc + url = https://github.com/discordapp/discord-rpc.git +[submodule "ext/rapidjson"] + path = ext/rapidjson + url = https://github.com/Tencent/rapidjson.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 534d4e9b9d..1b874b8721 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,7 @@ option(SIMULATOR "Set to ON when targeting an x86 simulator of an ARM platform" option(LIBRETRO "Set to ON to generate the libretro target" OFF) # :: Options option(USE_FFMPEG "Build with FFMPEG support" ${USE_FFMPEG}) +option(USE_SYSTEM_SNAPPY "Dynamically link against system snappy" ${USE_SYSTEM_SNAPPY}) option(USE_SYSTEM_FFMPEG "Dynamically link against system FFMPEG" ${USE_SYSTEM_FFMPEG}) option(USE_SYSTEM_LIBZIP "Dynamically link against system libzip" ${USE_SYSTEM_LIBZIP}) option(USE_WAYLAND_WSI "Set to ON to require Wayland support for Vulkan" ${USE_WAYLAND_WSI}) @@ -822,6 +823,7 @@ endif() list(APPEND NativeAppSource android/jni/TestRunner.cpp + UI/DiscordIntegration.cpp UI/NativeApp.cpp UI/BackgroundAudio.cpp UI/DevScreens.cpp @@ -1609,6 +1611,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/sceUmd.h Core/HLE/sceUsb.cpp Core/HLE/sceUsb.h + Core/HLE/sceUsbAcc.cpp + Core/HLE/sceUsbAcc.h Core/HLE/sceUsbCam.cpp Core/HLE/sceUsbCam.h Core/HLE/sceUsbGps.cpp @@ -1754,6 +1758,11 @@ if(FFmpeg_FOUND) ) endif() +# Discord integration +if(NOT IOS) +target_link_libraries(${CoreLibName} discord-rpc) +endif() + setup_target_project(${CoreLibName} Core) # Generate git-version.cpp at build time. diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index cf033d00ea..1c378870d1 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -80,7 +80,6 @@ true false false - ProgramDatabase Windows @@ -108,7 +107,6 @@ true false false - ProgramDatabase Windows diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 1a9629f35b..88581332c7 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -24,6 +24,7 @@ #include "ppsspp_config.h" +#include #include "FileUtil.h" #include "StringUtils.h" @@ -100,6 +101,66 @@ bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::o return stream.is_open(); } +std::string ResolvePath(const std::string &path) { +#ifdef _WIN32 + typedef DWORD (WINAPI *getFinalPathNameByHandleW_f)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); + static getFinalPathNameByHandleW_f getFinalPathNameByHandleW = nullptr; + + if (!getFinalPathNameByHandleW) { + // We leak this, but that's okay since the process should hold onto this DLL for the entire lifetime anyway. + HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); + getFinalPathNameByHandleW = (getFinalPathNameByHandleW_f)GetProcAddress(kernel32, "GetFinalPathNameByHandleW"); + } + + static const int BUF_SIZE = 32768; + wchar_t *buf = new wchar_t[BUF_SIZE]; + memset(buf, 0, BUF_SIZE); + + std::wstring input = ConvertUTF8ToWString(path); + if (getFinalPathNameByHandleW) { + HANDLE hFile = CreateFile(input.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + wcscpy_s(buf, BUF_SIZE - 1, input.c_str()); + } else { + int result = getFinalPathNameByHandleW(hFile, buf, BUF_SIZE - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (result >= BUF_SIZE || result == 0) + wcscpy_s(buf, BUF_SIZE - 1, input.c_str()); + } + } else { + wchar_t *longBuf = new wchar_t[BUF_SIZE]; + memset(buf, 0, BUF_SIZE); + + int result = GetLongPathNameW(input.c_str(), longBuf, BUF_SIZE - 1); + if (result >= BUF_SIZE || result == 0) + wcscpy_s(longBuf, BUF_SIZE - 1, input.c_str()); + + result = GetFullPathNameW(longBuf, BUF_SIZE - 1, buf, nullptr); + if (result >= BUF_SIZE || result == 0) + wcscpy_s(buf, BUF_SIZE - 1, input.c_str()); + + delete [] longBuf; + + // Normalize slashes just in case. + for (int i = 0; i < BUF_SIZE; ++i) { + if (buf[i] == '\\') + buf[i] = '/'; + } + } + + // Undo the \\?\C:\ syntax that's normally returned. + std::string output = ConvertWStringToUTF8(buf); + if (buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' && isalpha(buf[4]) && buf[5] == ':') + output = output.substr(4); + delete [] buf; + return output; + +#else + std::unique_ptr buf(new char[PATH_MAX + 32768]); + if (realpath(path.c_str(), buf.get()) == nullptr) + return path; + return buf.get(); +#endif +} // Remove any ending forward slashes from directory paths // Modifies argument. diff --git a/Common/FileUtil.h b/Common/FileUtil.h index 07ac398fb7..31e0e7a373 100644 --- a/Common/FileUtil.h +++ b/Common/FileUtil.h @@ -58,6 +58,9 @@ struct FileDetails { FILE *OpenCFile(const std::string &filename, const char *mode); bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::openmode mode); +// Resolves symlinks and similar. +std::string ResolvePath(const std::string &path); + // Returns true if file filename exists bool Exists(const std::string &filename); diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index 65545b1fc0..485a7b47da 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -99,7 +99,7 @@ static const LogNameTableEntry logTable[] = { {LogTypes::SCECTRL, "SCECTRL"}, {LogTypes::SCEDISPLAY, "SCEDISP"}, {LogTypes::SCEFONT, "SCEFONT"}, - {LogTypes::SCEGE, "SCESCEGE"}, + {LogTypes::SCEGE, "SCEGE"}, {LogTypes::SCEINTC, "SCEINTC"}, {LogTypes::SCEIO, "SCEIO"}, {LogTypes::SCEKERNEL, "SCEKERNEL"}, diff --git a/Core/Config.cpp b/Core/Config.cpp index cb80719748..07929c3ca4 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -400,6 +400,7 @@ static ConfigSetting generalSettings[] = { ConfigSetting("CheckForNewVersion", &g_Config.bCheckForNewVersion, false), ConfigSetting("Language", &g_Config.sLanguageIni, &DefaultLangRegion), ConfigSetting("ForceLagSync", &g_Config.bForceLagSync, false, true, true), + ConfigSetting("DiscordPresence", &g_Config.bDiscordPresence, false, false, false), // Or maybe it makes sense to have it per-game? Race conditions abound... ReportedConfigSetting("NumWorkerThreads", &g_Config.iNumWorkerThreads, &DefaultNumWorkers, true, true), ConfigSetting("AutoLoadSaveState", &g_Config.iAutoLoadSaveState, 0, true, true), @@ -1175,8 +1176,8 @@ void Config::DownloadCompletedCallback(http::Download &download) { return; } - JsonReader reader(data.c_str(), data.size()); - const JsonGet root = reader.root(); + json::JsonReader reader(data.c_str(), data.size()); + const json::JsonGet root = reader.root(); if (!root) { ERROR_LOG(LOADER, "Failed to parse json"); return; @@ -1222,30 +1223,32 @@ void Config::AddRecent(const std::string &file) { if (iMaxRecent <= 0) return; -#ifdef _WIN32 - std::string filename = ReplaceAll(file, "\\", "/"); -#else - std::string filename = file; -#endif + // We'll add it back below. This makes sure it's at the front, and only once. + RemoveRecent(file); - for (auto str = recentIsos.begin(); str != recentIsos.end(); ++str) { -#ifdef _WIN32 - if (!strcmpIgnore((*str).c_str(), filename.c_str(), "\\", "/")) { -#else - if (!strcmp((*str).c_str(), filename.c_str())) { -#endif - recentIsos.erase(str); - recentIsos.insert(recentIsos.begin(), filename); - if ((int)recentIsos.size() > iMaxRecent) - recentIsos.resize(iMaxRecent); - return; - } - } + const std::string filename = File::ResolvePath(file); recentIsos.insert(recentIsos.begin(), filename); if ((int)recentIsos.size() > iMaxRecent) recentIsos.resize(iMaxRecent); } +void Config::RemoveRecent(const std::string &file) { + // Don't bother with this if the user disabled recents (it's -1). + if (iMaxRecent <= 0) + return; + + const std::string filename = File::ResolvePath(file); + for (auto iter = recentIsos.begin(); iter != recentIsos.end();) { + const std::string recent = File::ResolvePath(*iter); + if (filename == recent) { + // Note that the increment-erase idiom doesn't work with vectors. + iter = recentIsos.erase(iter); + } else { + iter++; + } + } +} + void Config::CleanRecent() { std::vector cleanedRecent; for (size_t i = 0; i < recentIsos.size(); i++) { diff --git a/Core/Config.h b/Core/Config.h index 4c04af37b5..c3cfb75ac3 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -124,6 +124,8 @@ public: std::vector vPinnedPaths; std::string sLanguageIni; + bool bDiscordPresence; // Enables setting the Discord presence to the current game (or menu) + // GFX int iGPUBackend; // We have separate device parameters for each backend so it doesn't get erased if you switch backends. @@ -441,6 +443,7 @@ public: // Utility functions for "recent" management void AddRecent(const std::string &file); + void RemoveRecent(const std::string &file); void CleanRecent(); static void DownloadCompletedCallback(http::Download &download); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c0751c917a..068241f0c6 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -78,7 +78,6 @@ false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h @@ -101,7 +100,6 @@ false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h @@ -198,6 +196,7 @@ + @@ -560,6 +559,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index b398758157..ba79121304 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -731,6 +731,9 @@ Debugger\WebSocket + + HLE\Libraries + @@ -1355,6 +1358,9 @@ Core + + HLE\Libraries + diff --git a/Core/Debugger/WebSocket/WebSocketUtils.h b/Core/Debugger/WebSocket/WebSocketUtils.h index e49647dcf8..70c08828b2 100644 --- a/Core/Debugger/WebSocket/WebSocketUtils.h +++ b/Core/Debugger/WebSocket/WebSocketUtils.h @@ -24,6 +24,8 @@ #include "net/websocket_server.h" #include "Common/Log.h" +using namespace json; + static inline void DebuggerJsonAddTicket(JsonWriter &writer, const JsonGet &data) { const JsonNode *value = data.get("ticket"); if (value) diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index cb68396f6a..e47f51dcf7 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -1101,21 +1101,14 @@ void PSPSaveDialog::ExecuteNotVisibleIOAction() { // TODO: Should reset the directory's other files. case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA: case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE: - if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE) == 0) { - result = 0; - } else if (MemoryStick_FreeSpace() == 0) { + result = param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE); + if (result == SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE) { result = SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL; - } else { - result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; } break; case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA: case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE: - if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE) == 0) { - result = 0; - } else { - result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; - } + result = param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE); break; case SCE_UTILITY_SAVEDATA_TYPE_READDATA: case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE: diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 3182f3b3df..a137e779f7 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -362,6 +362,21 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD if (!param) { return SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE; } + if (param->dataSize > param->dataBufSize) { + ERROR_LOG_REPORT(SCEUTILITY, "Savedata buffer overflow: %d / %d", param->dataSize, param->dataBufSize); + return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS; + } + auto validateSize = [](const PspUtilitySavedataFileData &data) { + if (data.buf.IsValid() && data.bufSize < data.size) { + ERROR_LOG_REPORT(SCEUTILITY, "Savedata subdata buffer overflow: %d / %d", data.size, data.bufSize); + return false; + } + return true; + }; + if (!validateSize(param->icon0FileData) || !validateSize(param->icon1FileData) || !validateSize(param->pic1FileData) || !validateSize(param->snd0FileData)) { + return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS; + } + if (param->secureVersion > 3) { ERROR_LOG_REPORT(SCEUTILITY, "Savedata version requested on save: %d", param->secureVersion); return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM; @@ -520,26 +535,26 @@ int SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &saveD if (param->icon0FileData.buf.IsValid()) { std::string icon0path = dirPath + "/" + ICON0_FILENAME; - WritePSPFile(icon0path, param->icon0FileData.buf, param->icon0FileData.bufSize); + WritePSPFile(icon0path, param->icon0FileData.buf, param->icon0FileData.size); } // SAVE ICON1 if (param->icon1FileData.buf.IsValid()) { std::string icon1path = dirPath + "/" + ICON1_FILENAME; - WritePSPFile(icon1path, param->icon1FileData.buf, param->icon1FileData.bufSize); + WritePSPFile(icon1path, param->icon1FileData.buf, param->icon1FileData.size); } // SAVE PIC1 if (param->pic1FileData.buf.IsValid()) { std::string pic1path = dirPath + "/" + PIC1_FILENAME; - WritePSPFile(pic1path, param->pic1FileData.buf, param->pic1FileData.bufSize); + WritePSPFile(pic1path, param->pic1FileData.buf, param->pic1FileData.size); } // Save SND if (param->snd0FileData.buf.IsValid()) { std::string snd0path = dirPath + "/" + SND0_FILENAME; - WritePSPFile(snd0path, param->snd0FileData.buf, param->snd0FileData.bufSize); + WritePSPFile(snd0path, param->snd0FileData.buf, param->snd0FileData.size); } return 0; @@ -642,20 +657,28 @@ int SavedataParam::DetermineCryptMode(const SceUtilitySavedataParam *param) cons return decryptMode; } -void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone) { +void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, const u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone) { + int orig_size = saveSize; int align_len = align16(saveSize); u8 *data_base = new u8[align_len]; u8 *cryptKey = new u8[0x10]; - memset(cryptKey, 0, 0x10); int decryptMode = DetermineCryptMode(param); const int detectedMode = decryptMode; - bool hasKey = decryptMode > 1; - if (hasKey) { - memcpy(cryptKey, param->key, 0x10); - } - memset(data_base + saveSize, 0, align_len - saveSize); - memcpy(data_base, saveData, saveSize); + bool hasKey; + + auto resetData = [&](int mode) { + saveSize = orig_size; + align_len = align16(saveSize); + hasKey = mode > 1; + + if (hasKey) { + memcpy(cryptKey, param->key, 0x10); + } + memcpy(data_base, saveData, saveSize); + memset(data_base + saveSize, 0, align_len - saveSize); + }; + resetData(decryptMode); if (decryptMode != prevCryptMode) { if (prevCryptMode == 1 && param->key[0] == 0) { @@ -688,12 +711,13 @@ void SavedataParam::LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 int err = DecryptSave(decryptMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, expectedHash); // Perhaps the file had the wrong mode.... if (err != 0 && detectedMode != decryptMode) { - hasKey = detectedMode > 1; + resetData(detectedMode); err = DecryptSave(detectedMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, expectedHash); } // TODO: Should return an error, but let's just try with a bad hash. if (err != 0 && expectedHash != nullptr) { WARN_LOG(SCEUTILITY, "Incorrect hash on save data, likely corrupt"); + resetData(decryptMode); err = DecryptSave(decryptMode, data_base, &saveSize, &align_len, hasKey ? cryptKey : nullptr, nullptr); } diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 0a26f1aeb1..e509463712 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -361,7 +361,7 @@ private: void ClearFileInfo(SaveFileInfo &saveInfo, const std::string &saveName); int LoadSaveData(SceUtilitySavedataParam *param, const std::string &saveDirName, const std::string& dirPath, bool secureMode); - void LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone); + void LoadCryptedSave(SceUtilitySavedataParam *param, u8 *data, const u8 *saveData, int &saveSize, int prevCryptMode, const u8 *expectedHash, bool &saveDone); void LoadNotCryptedSave(SceUtilitySavedataParam *param, u8 *data, u8 *saveData, int &saveSize); void LoadSFO(SceUtilitySavedataParam *param, const std::string& dirPath); void LoadFile(const std::string& dirPath, const std::string& filename, PspUtilitySavedataFileData *fileData); diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 71e91d06d8..27a15f352e 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -825,6 +825,8 @@ static std::string SimulateVFATBug(std::string filename) { std::vector DirectoryFileSystem::GetDirListing(std::string path) { std::vector myVector; + bool listingRoot = path == "/" || path == "\\"; + #ifdef _WIN32 WIN32_FIND_DATA findData; HANDLE hFind; @@ -849,7 +851,7 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { entry.access = entry.type == FILETYPE_NORMAL ? 0666 : 0777; // TODO: is this just for .. or all subdirectories? Need to add a directory to the test // to find out. Also why so different than the old test results? - if (!wcscmp(findData.cFileName, L"..") ) + if (!wcscmp(findData.cFileName, L"..")) entry.size = 4096; else entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32); @@ -857,7 +859,8 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { tmFromFiletime(entry.atime, findData.ftLastAccessTime); tmFromFiletime(entry.ctime, findData.ftCreationTime); tmFromFiletime(entry.mtime, findData.ftLastWriteTime); - myVector.push_back(entry); + if (!listingRoot || (wcscmp(findData.cFileName, L"..") && wcscmp(findData.cFileName, L"."))) + myVector.push_back(entry); int retval = FindNextFile(hFind, &findData); if (!retval) @@ -897,7 +900,8 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { localtime_r((time_t*)&s.st_atime,&entry.atime); localtime_r((time_t*)&s.st_ctime,&entry.ctime); localtime_r((time_t*)&s.st_mtime,&entry.mtime); - myVector.push_back(entry); + if (!listingRoot || (strcmp(dirp->d_name, "..") && strcmp(dirp->d_name, "."))) + myVector.push_back(entry); } closedir(dp); #endif diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index 5d555e4adc..b07946ebf9 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -44,7 +44,7 @@ VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, std::str : basePath(_basePath),currentBlockIndex(0) { #ifdef _WIN32 - if (!endsWith(basePath, "\\")) + if (!endsWith(basePath, "\\") && !endsWith(basePath, "/")) basePath = basePath + "\\"; #else if (!endsWith(basePath, "/")) diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 129fc69354..ed1331f3ad 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -66,6 +66,7 @@ #include "sceSsl.h" #include "sceUmd.h" #include "sceUsb.h" +#include "sceUsbAcc.h" #include "sceUsbCam.h" #include "sceUsbGps.h" #include "sceUtility.h" @@ -296,6 +297,7 @@ void RegisterAllModules() { Register_sceNetUpnp(); Register_sceNetIfhandle(); Register_KUBridge(); + Register_sceUsbAcc(); // add new modules here. } diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index fad468bfa8..dafa5d09b1 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -353,10 +353,10 @@ u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, int callbackId, u32 auto optParam = PSPPointer::Create(optParamAddr); u32 listID = gpu->EnqueueList(listAddress, stallAddress, __GeSubIntrBase(callbackId), optParam, true); - if ((int)listID >= 0) + if ((int)listID >= 0) { listID = LIST_ID_MAGIC ^ listID; - - DEBUG_LOG(SCEGE, "List %i enqueued at head.", listID); + DEBUG_LOG(SCEGE, "List %i enqueued at head.", listID); + } hleEatCycles(480); CoreTiming::ForceCheck(); return listID; diff --git a/Core/HLE/sceUsbAcc.cpp b/Core/HLE/sceUsbAcc.cpp new file mode 100644 index 0000000000..93d6add3cb --- /dev/null +++ b/Core/HLE/sceUsbAcc.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/HLE/HLE.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceUsbAcc.h" + +// Assuming https://github.com/joel16/usbacc/blob/master/usbacc.c is correct +// sceUsbAccGetAuthStat should return 0 or one of the two errors on failure +// sceUsbAccGetInfo should return 0 or one of the 3 errors on failure +// we don't have to deal with physical Usb connection, so let's just always pass + +static int sceUsbAccGetAuthStat() { + INFO_LOG(HLE, "UNIMPL sceUsbAccGetAuthStat"); + return 0; +} + +static int sceUsbAccGetInfo(u32 addr) { + INFO_LOG(HLE, "UNIMPL sceUsbAccGetInfo"); + return 0; +} + +const HLEFunction sceUsbAcc[] = +{ + {0X79A1C743, &WrapI_V, "sceUsbAccGetAuthStat", 'i', "" }, + {0X0CD7D4AA, &WrapI_U, "sceUsbAccGetInfo", 'i', "x" }, +}; + +void Register_sceUsbAcc() +{ + RegisterModule("sceUsbAcc", ARRAY_SIZE(sceUsbAcc), sceUsbAcc); +} diff --git a/Core/HLE/sceUsbAcc.h b/Core/HLE/sceUsbAcc.h new file mode 100644 index 0000000000..da84904d1f --- /dev/null +++ b/Core/HLE/sceUsbAcc.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +void Register_sceUsbAcc(); + diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index be32edfb6d..4d85c51a98 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -544,6 +544,8 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer if (!useBufferedRendering_) { // Let's ignore rendering to targets that have not (yet) been displayed. gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB; + } else if (currentRenderVfb_) { + DownloadFramebufferOnSwitch(currentRenderVfb_); } textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED); @@ -584,6 +586,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe DownloadFramebufferOnSwitch(prevVfb); } textureCache_->ForgetLastTexture(); + shaderManager_->DirtyLastShader(); // Copy depth pixel value from the read framebuffer to the draw framebuffer if (prevVfb && !g_Config.bDisableSlowFramebufEffects) { @@ -602,6 +605,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe if (useBufferedRendering_) { if (vfb->fbo) { + shaderManager_->DirtyLastShader(); if (gl_extensions.IsGLES) { // Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering // to it. This broke stuff before, so now it only clears on the first use of an @@ -841,6 +845,7 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) { void FramebufferManagerCommon::CopyDisplayToOutput() { DownloadFramebufferOnSwitch(currentRenderVfb_); + shaderManager_->DirtyLastShader(); currentRenderVfb_ = 0; @@ -848,6 +853,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { DEBUG_LOG(FRAMEBUF, "Display disabled, displaying only black"); // No framebuffer to display! Clear to black. if (useBufferedRendering_) { + shaderManager_->DirtyLastShader(); draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); } gstate_c.Dirty(DIRTY_BLEND_STATE); @@ -912,6 +918,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { } if (!vfb) { + shaderManager_->DirtyLastShader(); if (useBufferedRendering_) { // Bind and clear the backbuffer. This should be the first time during the frame that it's bound. draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); @@ -927,6 +934,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { DEBUG_LOG(FRAMEBUF, "Found no FBO to display! displayFBPtr = %08x", displayFramebufPtr_); // No framebuffer to display! Clear to black. if (useBufferedRendering_) { + shaderManager_->DirtyLastShader(); // Bind and clear the backbuffer. This should be the first time during the frame that it's bound. draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); } @@ -965,6 +973,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { float v1 = (272.0f + offsetY) / (float)vfb->bufferHeight; if (!usePostShader_) { + shaderManager_->DirtyLastShader(); draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0); draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); @@ -989,13 +998,13 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { } } else if (usePostShader_ && extraFBOs_.size() == 1 && !postShaderAtOutputResolution_) { // An additional pass, post-processing shader to the extra FBO. + shaderManager_->DirtyLastShader(); // dirty lastShader_ draw_->BindFramebufferAsRenderTarget(extraFBOs_[0], { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0); int fbo_w, fbo_h; draw_->GetFramebufferDimensions(extraFBOs_[0], &fbo_w, &fbo_h); SetViewport2D(0, 0, fbo_w, fbo_h); draw_->SetScissorRect(0, 0, fbo_w, fbo_h); - shaderManager_->DirtyLastShader(); // dirty lastShader_ PostShaderUniforms uniforms{}; CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms); BindPostShader(uniforms); @@ -1033,6 +1042,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); } } else { + shaderManager_->DirtyLastShader(); // dirty lastShader_ BEFORE drawing draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0); draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); @@ -1042,7 +1052,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { std::swap(v0, v1); DrawTextureFlags flags = (!postShaderIsUpscalingFilter_ && g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST; - shaderManager_->DirtyLastShader(); // dirty lastShader_ BEFORE drawing PostShaderUniforms uniforms{}; CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, vfb->renderWidth, vfb->renderHeight, &uniforms); BindPostShader(uniforms); @@ -1183,6 +1192,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, return; } + shaderManager_->DirtyLastShader(); vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); if (old.fbo) { INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %d x %d x %d", vfb->fb_address, w, h, vfb->format); @@ -2130,6 +2140,7 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo } void FramebufferManagerCommon::RebindFramebuffer() { + shaderManager_->DirtyLastShader(); if (currentRenderVfb_ && currentRenderVfb_->fbo) { draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }); } else { diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index cd337f3bad..da6a5f67a9 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -40,20 +40,28 @@ bool CanUseHardwareTransform(int prim) { return !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES; } -// Dest factors where it's safe to eliminate the alpha test under certain conditions -static const bool safeDestFactors[16] = { - true, // GE_DSTBLEND_SRCCOLOR, - true, // GE_DSTBLEND_INVSRCCOLOR, - false, // GE_DSTBLEND_SRCALPHA, - true, // GE_DSTBLEND_INVSRCALPHA, - true, // GE_DSTBLEND_DSTALPHA, - true, // GE_DSTBLEND_INVDSTALPHA, - false, // GE_DSTBLEND_DOUBLESRCALPHA, - false, // GE_DSTBLEND_DOUBLEINVSRCALPHA, - true, // GE_DSTBLEND_DOUBLEDSTALPHA, - true, // GE_DSTBLEND_DOUBLEINVDSTALPHA, - true, //GE_DSTBLEND_FIXB, -}; +bool NeedsTestDiscard() { + // We assume this is called only when enabled and not trivially true (may also be for color testing.) + if (gstate.isStencilTestEnabled() && (gstate.pmska & 0xFF) != 0xFF) + return true; + if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) + return true; + if (!gstate.isAlphaBlendEnabled()) + return true; + if (gstate.getBlendFuncA() != GE_SRCBLEND_SRCALPHA && gstate.getBlendFuncA() != GE_SRCBLEND_DOUBLESRCALPHA) + return true; + // GE_DSTBLEND_DOUBLEINVSRCALPHA is actually inverse double src alpha, and doubling zero is still zero. + if (gstate.getBlendFuncB() != GE_DSTBLEND_INVSRCALPHA && gstate.getBlendFuncB() != GE_DSTBLEND_DOUBLEINVSRCALPHA) { + if (gstate.getBlendFuncB() != GE_DSTBLEND_FIXB || gstate.getFixB() != 0xFFFFFF) + return true; + } + if (gstate.getBlendEq() != GE_BLENDMODE_MUL_AND_ADD && gstate.getBlendEq() != GE_BLENDMODE_MUL_AND_SUBTRACT_REVERSE) + return true; + if (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY) + return true; + + return false; +} bool IsAlphaTestTriviallyTrue() { switch (gstate.getAlphaTestFunction()) { @@ -80,25 +88,10 @@ bool IsAlphaTestTriviallyTrue() { case GE_COMP_GREATER: { -#if 0 - // Easy way to check the values in the debugger without ruining && early-out - bool doTextureAlpha = gstate.isTextureAlphaUsed(); - bool stencilTest = gstate.isStencilTestEnabled(); - bool depthTest = gstate.isDepthTestEnabled(); - GEComparison depthTestFunc = gstate.getDepthTestFunction(); - int alphaRef = gstate.getAlphaTestRef(); - int blendA = gstate.getBlendFuncA(); - bool blendEnabled = gstate.isAlphaBlendEnabled(); - int blendB = gstate.getBlendFuncA(); -#endif - return (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed())) || ( - (!gstate.isStencilTestEnabled() && - !gstate.isDepthTestEnabled() && - (!gstate.isLogicOpEnabled() || gstate.getLogicOp() == GE_LOGIC_COPY) && - gstate.getAlphaTestRef() == 0 && - gstate.isAlphaBlendEnabled() && - gstate.getBlendFuncA() == GE_SRCBLEND_SRCALPHA && - safeDestFactors[(int)gstate.getBlendFuncB()])); + // If the texture and vertex only use 1.0 alpha, then the ref value doesn't matter. + if (gstate_c.vertexFullAlpha && (gstate_c.textureFullAlpha || !gstate.isTextureAlphaUsed())) + return true; + return gstate.getAlphaTestRef() == 0 && !NeedsTestDiscard(); } case GE_COMP_LEQUAL: @@ -684,7 +677,7 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo float minz = gstate.getDepthRangeMin(); float maxz = gstate.getDepthRangeMax(); - if (gstate.isClippingEnabled() && (minz == 0 || maxz == 65535)) { + if (gstate.isDepthClampEnabled() && (minz == 0 || maxz == 65535)) { // Here, we should "clamp." But clamping per fragment would be slow. // So, instead, we just increase the available range and hope. // If depthSliceFactor is 4, it means (75% / 2) of the depth lies in each direction. @@ -821,22 +814,16 @@ static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) { } // Try to simulate some common logic ops. -void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) { - StencilValueType stencilType = STENCIL_VALUE_KEEP; - if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { - stencilType = ReplaceAlphaWithStencilType(); - } - - // Normally, we would add src + 0, but the logic op may have us do differently. - BlendFactor srcBlend = BlendFactor::ONE; - BlendFactor dstBlend = BlendFactor::ZERO; - BlendEq blendEq = BlendEq::ADD; - +static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { + // Note: our shader solution applies logic ops BEFORE blending, not correctly after. + // This is however fine for the most common ones, like CLEAR/NOOP/SET, etc. if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { if (gstate.isLogicOpEnabled()) { switch (gstate.getLogicOp()) { case GE_LOGIC_CLEAR: srcBlend = BlendFactor::ZERO; + dstBlend = BlendFactor::ZERO; + blendEq = BlendEq::ADD; break; case GE_LOGIC_AND: case GE_LOGIC_AND_REVERSE: @@ -864,6 +851,7 @@ void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, Gen case GE_LOGIC_NOOP: srcBlend = BlendFactor::ZERO; dstBlend = BlendFactor::ONE; + blendEq = BlendEq::ADD; break; case GE_LOGIC_XOR: WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp()); @@ -878,14 +866,30 @@ void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, Gen WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp()); break; case GE_LOGIC_SET: + srcBlend = BlendFactor::ONE; dstBlend = BlendFactor::ONE; + blendEq = BlendEq::ADD; WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp()); break; } } } +} - // We're not blending, but we may still want to blend for stencil. +// Try to simulate some common logic ops. +void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) { + StencilValueType stencilType = STENCIL_VALUE_KEEP; + if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { + stencilType = ReplaceAlphaWithStencilType(); + } + + // Normally, we would add src + 0 with blending off, but the logic op may have us do differently. + BlendFactor srcBlend = BlendFactor::ONE; + BlendFactor dstBlend = BlendFactor::ZERO; + BlendEq blendEq = BlendEq::ADD; + ApplyLogicOp(srcBlend, dstBlend, blendEq); + + // We're not blending, but we may still want to "blend" for stencil. // This is only useful for INCR/DECR/INVERT. Others can write directly. switch (stencilType) { case STENCIL_VALUE_INCR_4: @@ -1113,6 +1117,15 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) { #endif // At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set right somehow. + BlendEq colorEq; + if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) { + colorEq = eqLookup[blendFuncEq]; + } else { + colorEq = eqLookupNoMinMax[blendFuncEq]; + } + + // Attempt to apply the logic op, if any. + ApplyLogicOp(glBlendFuncA, glBlendFuncB, colorEq); // The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't // do any blending in the alpha channel as that doesn't seem to happen on PSP. So, we attempt to @@ -1182,11 +1195,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) { blendState.setFactors(glBlendFuncA, glBlendFuncB, BlendFactor::ZERO, BlendFactor::ONE); } - if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) { - blendState.setEquation(eqLookup[blendFuncEq], alphaEq); - } else { - blendState.setEquation(eqLookupNoMinMax[blendFuncEq], alphaEq); - } + blendState.setEquation(colorEq, alphaEq); } static void ConvertStencilFunc5551(GenericStencilFuncState &state) { diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index e6b231d8e1..f6a4e11c21 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -44,6 +44,7 @@ bool IsAlphaTestTriviallyTrue(); bool IsColorTestAgainstZero(); bool IsColorTestTriviallyTrue(); bool IsAlphaTestAgainstZero(); +bool NeedsTestDiscard(); StencilValueType ReplaceAlphaWithStencilType(); ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend); diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 4a33f7c216..9a6d9afb82 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -268,12 +268,15 @@ void ComputeFragmentShaderID(ShaderID *id_out) { id.SetBit(FS_BIT_ALPHA_TEST); id.SetBits(FS_BIT_ALPHA_TEST_FUNC, 3, gstate.getAlphaTestFunction()); id.SetBit(FS_BIT_ALPHA_AGAINST_ZERO, IsAlphaTestAgainstZero()); + id.SetBit(FS_BIT_TEST_DISCARD_TO_ZERO, !NeedsTestDiscard()); } if (enableColorTest) { // 4 bits total. id.SetBit(FS_BIT_COLOR_TEST); id.SetBits(FS_BIT_COLOR_TEST_FUNC, 2, gstate.getColorTestFunction()); id.SetBit(FS_BIT_COLOR_AGAINST_ZERO, IsColorTestAgainstZero()); + // This is alos set in enableAlphaTest - color test is uncommon, but we can skip discard the same way. + id.SetBit(FS_BIT_TEST_DISCARD_TO_ZERO, !NeedsTestDiscard()); } id.SetBit(FS_BIT_ENABLE_FOG, enableFog); diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 7d2e1b1cdd..740e321fe8 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -86,7 +86,8 @@ enum { FS_BIT_BLENDFUNC_B = 42, // 4 bits FS_BIT_FLATSHADE = 46, FS_BIT_BGRA_TEXTURE = 47, - // 48+ are free. + FS_BIT_TEST_DISCARD_TO_ZERO = 48, + // 49+ are free. }; struct ShaderID { diff --git a/GPU/Common/ShaderTranslation.cpp b/GPU/Common/ShaderTranslation.cpp index b253a96f6b..862ba286c6 100644 --- a/GPU/Common/ShaderTranslation.cpp +++ b/GPU/Common/ShaderTranslation.cpp @@ -310,7 +310,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade spirv_cross::CompilerGLSL::Options options; options.version = 140; options.es = true; - glsl.set_options(options); + glsl.set_common_options(options); // Compile to GLSL, ready to give to GL driver. *dest = glsl.compile(); @@ -324,7 +324,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade // Set some options. spirv_cross::CompilerGLSL::Options options; options.version = gl_extensions.GLSLVersion(); - glsl.set_options(options); + glsl.set_common_options(options); // Compile to GLSL, ready to give to GL driver. *dest = glsl.compile(); return true; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index d14a79277f..b1fe68058b 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -720,6 +720,9 @@ void TextureCacheCommon::DetachFramebuffer(TexCacheEntry *entry, u32 address, Vi const u64 cachekey = entry->CacheKey(); cacheSizeEstimate_ += EstimateTexMemoryUsage(entry); entry->framebuffer = nullptr; + // Force the hash to change in case we had one before. + // Otherwise we never recreate the texture. + entry->hash ^= 1; fbTexInfo_.erase(cachekey); host->GPUNotifyTextureAttachment(entry->addr); } diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index dff4125a9e..c5ba24d893 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -152,6 +152,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform bool enableAlphaTest = id.Bit(FS_BIT_ALPHA_TEST); bool alphaTestAgainstZero = id.Bit(FS_BIT_ALPHA_AGAINST_ZERO); + bool testForceToZero = id.Bit(FS_BIT_TEST_DISCARD_TO_ZERO); bool enableColorTest = id.Bit(FS_BIT_COLOR_TEST); bool colorTestAgainstZero = id.Bit(FS_BIT_COLOR_AGAINST_ZERO); bool enableColorDoubling = id.Bit(FS_BIT_COLOR_DOUBLE); @@ -346,29 +347,37 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform doTextureProjection = false; } - if (doTextureProjection) { - WRITE(p, " vec4 t = %sProj(tex, %s);\n", texture, texcoord); - if (shaderDepal) { - WRITE(p, " vec4 t1 = %sProjOffset(tex, %s, ivec2(1, 0));\n", texture, texcoord); - WRITE(p, " vec4 t2 = %sProjOffset(tex, %s, ivec2(0, 1));\n", texture, texcoord); - WRITE(p, " vec4 t3 = %sProjOffset(tex, %s, ivec2(1, 1));\n", texture, texcoord); + if (!shaderDepal) { + if (doTextureProjection) { + WRITE(p, " vec4 t = %sProj(tex, %s);\n", texture, texcoord); + } else { + WRITE(p, " vec4 t = %s(tex, %s.xy);\n", texture, texcoord); } } else { - WRITE(p, " vec4 t = %s(tex, %s.xy);\n", texture, texcoord); - if (shaderDepal) { - WRITE(p, " vec4 t1 = %sOffset(tex, %s.xy, ivec2(1, 0));\n", texture, texcoord); - WRITE(p, " vec4 t2 = %sOffset(tex, %s.xy, ivec2(0, 1));\n", texture, texcoord); - WRITE(p, " vec4 t3 = %sOffset(tex, %s.xy, ivec2(1, 1));\n", texture, texcoord); + if (doTextureProjection) { + // We don't use textureProj because we need better control and it's probably not much of a savings anyway. + WRITE(p, " vec2 uv = %s.xy/%s.z;\n vec2 uv_round;\n", texcoord, texcoord); + } else { + WRITE(p, " vec2 uv = %s.xy;\n vec2 uv_round;\n", texcoord); } - } - - if (shaderDepal) { + WRITE(p, " vec2 tsize = vec2(textureSize(tex, 0));\n"); + WRITE(p, " vec2 fraction;\n"); + WRITE(p, " bool bilinear = (u_depal >> 31) != 0;\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " uv_round = uv * tsize - vec2(0.5, 0.5);\n"); + WRITE(p, " fraction = fract(uv_round);\n"); + WRITE(p, " uv_round = (uv_round - fraction + vec2(0.5, 0.5)) / tsize;\n"); // We want to take our four point samples at pixel centers. + WRITE(p, " } else {\n"); + WRITE(p, " uv_round = uv;\n"); + WRITE(p, " }\n"); + WRITE(p, " vec4 t = %s(tex, uv_round);\n", texture); + WRITE(p, " vec4 t1 = %sOffset(tex, uv_round, ivec2(1, 0));\n", texture); + WRITE(p, " vec4 t2 = %sOffset(tex, uv_round, ivec2(0, 1));\n", texture); + WRITE(p, " vec4 t3 = %sOffset(tex, uv_round, ivec2(1, 1));\n", texture); WRITE(p, " int depalMask = (u_depal & 0xFF);\n"); WRITE(p, " int depalShift = ((u_depal >> 8) & 0xFF);\n"); WRITE(p, " int depalOffset = (((u_depal >> 16) & 0xFF) << 4);\n"); WRITE(p, " int depalFmt = ((u_depal >> 24) & 0x3);\n"); - WRITE(p, " bool bilinear = (u_depal >> 31) != 0;\n"); - WRITE(p, " vec2 fraction = fract(%s.xy * vec2(textureSize(tex, 0).xy));\n", texcoord); WRITE(p, " ivec4 col; int index0; int index1; int index2; int index3;\n"); WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. WRITE(p, " case 0:\n"); // 565 @@ -422,7 +431,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " };\n"); WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n"); - WRITE(p, " if (bilinear) {\n"); + WRITE(p, " if (bilinear && !(index0 == index1 && index1 == index2 && index2 == index3)) {\n"); WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n"); @@ -511,39 +520,40 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform } } + const char *discardStatement = testForceToZero ? "v.a = 0.0;" : "discard;"; if (enableAlphaTest) { if (alphaTestAgainstZero) { // When testing against 0 (extremely common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. if (alphaTestFunc == GE_COMP_NOTEQUAL || alphaTestFunc == GE_COMP_GREATER) { - WRITE(p, " if (v.a < 0.002) discard;\n"); + WRITE(p, " if (v.a < 0.002) %s\n", discardStatement); } else if (alphaTestFunc != GE_COMP_NEVER) { // Anything else is a test for == 0. Happens sometimes, actually... - WRITE(p, " if (v.a > 0.002) discard;\n"); + WRITE(p, " if (v.a > 0.002) %s\n", discardStatement); } else { // NEVER has been logged as used by games, although it makes little sense - statically failing. // Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here. - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } else if (g_Config.bFragmentTestCache) { WRITE(p, " float aResult = %s(testtex, vec2(%s, 0)).a;\n", texture, alphaTestXCoord.c_str()); - WRITE(p, " if (aResult < 0.5) discard;\n"); + WRITE(p, " if (aResult < 0.5) %s\n", discardStatement); } else { const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; if (alphaTestFuncs[alphaTestFunc][0] != '#') { if (bitwiseOps) { - WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s int(u_alphacolorref.a)) discard;\n", alphaTestFuncs[alphaTestFunc]); + WRITE(p, " if ((roundAndScaleTo255i(v.a) & u_alphacolormask.a) %s int(u_alphacolorref.a)) %s\n", alphaTestFuncs[alphaTestFunc], discardStatement); } else if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) { // Work around bad PVR driver problem where equality check + discard just doesn't work. if (alphaTestFunc != GE_COMP_NOTEQUAL) { - WRITE(p, " if (roundTo255thf(v.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + WRITE(p, " if (roundTo255thf(v.a) %s u_alphacolorref.a) %s\n", alphaTestFuncs[alphaTestFunc], discardStatement); } } else { - WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + WRITE(p, " if (roundAndScaleTo255f(v.a) %s u_alphacolorref.a) %s\n", alphaTestFuncs[alphaTestFunc], discardStatement); } } else { // This means NEVER. See above. - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } } @@ -553,14 +563,14 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform // When testing against 0 (common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. if (colorTestFunc == GE_COMP_NOTEQUAL) { - WRITE(p, " if (v.r < 0.002 && v.g < 0.002 && v.b < 0.002) discard;\n"); + WRITE(p, " if (v.r < 0.002 && v.g < 0.002 && v.b < 0.002) %s\n", discardStatement); } else if (colorTestFunc != GE_COMP_NEVER) { // Anything else is a test for == 0. - WRITE(p, " if (v.r > 0.002 || v.g > 0.002 || v.b > 0.002) discard;\n"); + WRITE(p, " if (v.r > 0.002 || v.g > 0.002 || v.b > 0.002) %s\n", discardStatement); } else { // NEVER has been logged as used by games, although it makes little sense - statically failing. // Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here. - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } else if (g_Config.bFragmentTestCache) { WRITE(p, " float rResult = %s(testtex, vec2(vScale256.r, 0)).r;\n", texture); @@ -568,10 +578,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " float bResult = %s(testtex, vec2(vScale256.b, 0)).b;\n", texture); if (colorTestFunc == GE_COMP_EQUAL) { // Equal means all parts must be equal. - WRITE(p, " if (rResult < 0.5 || gResult < 0.5 || bResult < 0.5) discard;\n"); + WRITE(p, " if (rResult < 0.5 || gResult < 0.5 || bResult < 0.5) %s\n", discardStatement); } else { // Not equal means any part must be not equal. - WRITE(p, " if (rResult < 0.5 && gResult < 0.5 && bResult < 0.5) discard;\n"); + WRITE(p, " if (rResult < 0.5 && gResult < 0.5 && bResult < 0.5) %s\n", discardStatement); } } else { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; @@ -581,14 +591,14 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); const char *maskedFragColor = "ivec3(v_scaled.r & u_alphacolormask.r, v_scaled.g & u_alphacolormask.g, v_scaled.b & u_alphacolormask.b)"; const char *maskedColorRef = "ivec3(int(u_alphacolorref.r) & u_alphacolormask.r, int(u_alphacolorref.g) & u_alphacolormask.g, int(u_alphacolorref.b) & u_alphacolormask.b)"; - WRITE(p, " if (%s %s %s) discard;\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef); + WRITE(p, " if (%s %s %s) %s\n", maskedFragColor, colorTestFuncs[colorTestFunc], maskedColorRef, discardStatement); } else if (gl_extensions.gpuVendor == GPU_VENDOR_IMGTEC) { - WRITE(p, " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) discard;\n", colorTestFuncs[colorTestFunc]); + WRITE(p, " if (roundTo255thv(v.rgb) %s u_alphacolorref.rgb) %s\n", colorTestFuncs[colorTestFunc], discardStatement); } else { - WRITE(p, " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) discard;\n", colorTestFuncs[colorTestFunc]); + WRITE(p, " if (roundAndScaleTo255v(v.rgb) %s u_alphacolorref.rgb) %s\n", colorTestFuncs[colorTestFunc], discardStatement); } } else { - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } } diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index f95bfb73ca..2dc5359a64 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -634,7 +634,7 @@ void ShaderManagerGLES::DirtyShader() { shaderSwitchDirtyUniforms_ = 0; } -void ShaderManagerGLES::DirtyLastShader() { // disables vertex arrays +void ShaderManagerGLES::DirtyLastShader() { lastShader_ = nullptr; lastVShaderSame_ = false; } diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index e609e3a744..5b14d38c6d 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -169,7 +169,7 @@ public: void DeviceRestore(Draw::DrawContext *draw); void DirtyShader(); - void DirtyLastShader() override; // disables vertex arrays + void DirtyLastShader() override; int GetNumVertexShaders() const { return (int)vsCache_.size(); } int GetNumFragmentShaders() const { return (int)fsCache_.size(); } diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index d8effd95ae..dee08b4577 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -110,6 +110,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe // Common when creating buffers, it's already 0. We're done. return false; } + shaderManagerGL_->DirtyLastShader(); // Let's not bother with the shader if it's just zero. if (dstBuffer->fbo) { diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 255b3c9193..02bb86fb42 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -444,7 +444,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram uint32_t clutMode = gstate.clutformat & 0xFFFFFF; bool useShaderDepal = false; if (g_Config.bShaderDepal) { - useShaderDepal = gstate_c.Supports(GPU_SUPPORTS_GLSL_ES_300); + useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer && gstate_c.Supports(GPU_SUPPORTS_GLSL_ES_300); } if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { @@ -471,11 +471,12 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram depal = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); } if (depal) { + shaderManager_->DirtyLastShader(); + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); GLRTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); Draw::Framebuffer *depalFBO = framebufferManagerGL_->GetTempFBO(TempFBO::DEPAL, framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }); - shaderManager_->DirtyLastShader(); render_->SetScissor(GLRect2D{ 0, 0, (int)framebuffer->renderWidth, (int)framebuffer->renderHeight }); render_->SetViewport(GLRViewport{ 0.0f, 0.0f, (float)framebuffer->renderWidth, (float)framebuffer->renderHeight, 0.0f, 1.0f }); @@ -866,6 +867,7 @@ bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); // Not a framebuffer, so let's assume these are right. + // TODO: But they may definitely not be, if the texture was scaled. int w = gstate.getTextureWidth(level); int h = gstate.getTextureHeight(level); diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 96fd523c37..296a2081b0 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -93,7 +93,6 @@ false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h @@ -116,7 +115,6 @@ false _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) MultiThreadedDebug - ProgramDatabase Common/DbgNew.h diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index b79551930d..5489559935 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -192,7 +192,7 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_GUARDBAND}, { GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, { GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, - { GE_CMD_CLIPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE }, + { GE_CMD_DEPTHCLAMPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE }, // Z clip { GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE | DIRTY_VIEWPORTSCISSOR_STATE }, @@ -420,6 +420,14 @@ void GPUCommon::UpdateCmdInfo() { cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType; } + + if (g_Config.bFastMemory) { + cmdInfo_[GE_CMD_JUMP].func = &GPUCommon::Execute_JumpFast; + cmdInfo_[GE_CMD_CALL].func = &GPUCommon::Execute_CallFast; + } else { + cmdInfo_[GE_CMD_JUMP].func = &GPUCommon::Execute_Jump; + cmdInfo_[GE_CMD_CALL].func = &GPUCommon::Execute_Call; + } } void GPUCommon::BeginHostFrame() { @@ -632,8 +640,10 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointerpc, target - 4); + currentList->pc = target - 4; // pc will be increased after we return, counteract that +} + +void GPUCommon::Execute_JumpFast(u32 op, u32 diff) { + const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); UpdatePC(currentList->pc, target - 4); currentList->pc = target - 4; // pc will be increased after we return, counteract that } @@ -1190,6 +1205,7 @@ void GPUCommon::Execute_BJump(u32 op, u32 diff) { currentList->pc = target - 4; // pc will be increased after we return, counteract that } else { ERROR_LOG_REPORT(G3D, "BJUMP to illegal address %08x - ignoring! data=%06x", target, op & 0x00FFFFFF); + UpdateState(GPUSTATE_ERROR); } } } @@ -1197,15 +1213,25 @@ void GPUCommon::Execute_BJump(u32 op, u32 diff) { void GPUCommon::Execute_Call(u32 op, u32 diff) { PROFILE_THIS_SCOPE("gpu_call"); - // Saint Seiya needs correct support for relative calls. - const u32 retval = currentList->pc + 4; const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); -#ifdef _DEBUG if (!Memory::IsValidAddress(target)) { ERROR_LOG_REPORT(G3D, "CALL to illegal address %08x - ignoring! data=%06x", target, op & 0x00FFFFFF); + UpdateState(GPUSTATE_ERROR); return; } -#endif + DoExecuteCall(target); +} + +void GPUCommon::Execute_CallFast(u32 op, u32 diff) { + PROFILE_THIS_SCOPE("gpu_call"); + + const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); + DoExecuteCall(target); +} + +void GPUCommon::DoExecuteCall(u32 target) { + // Saint Seiya needs correct support for relative calls. + const u32 retval = currentList->pc + 4; // Bone matrix optimization - many games will CALL a bone matrix (!). // We don't optimize during recording - so the matrix data gets recorded. @@ -1310,6 +1336,7 @@ void GPUCommon::Execute_End(u32 op, u32 diff) { u32 target = (((signal << 16) | enddata) & 0xFFFFFFFC) - 4; if (!Memory::IsValidAddress(target)) { ERROR_LOG_REPORT(G3D, "Signal with Jump: bad address. signal/end: %04x %04x", signal, enddata); + UpdateState(GPUSTATE_ERROR); } else { UpdatePC(currentList->pc, target); currentList->pc = target; @@ -1327,6 +1354,7 @@ void GPUCommon::Execute_End(u32 op, u32 diff) { ERROR_LOG_REPORT(G3D, "Signal with Call: stack full. signal/end: %04x %04x", signal, enddata); } else if (!Memory::IsValidAddress(target)) { ERROR_LOG_REPORT(G3D, "Signal with Call: bad address. signal/end: %04x %04x", signal, enddata); + UpdateState(GPUSTATE_ERROR); } else { // TODO: This might save/restore other state... auto &stackEntry = currentList->stack[currentList->stackptr++]; diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 33abc0126d..c7fb5a532d 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -125,8 +125,10 @@ public: void Execute_Iaddr(u32 op, u32 diff); void Execute_Origin(u32 op, u32 diff); void Execute_Jump(u32 op, u32 diff); + void Execute_JumpFast(u32 op, u32 diff); void Execute_BJump(u32 op, u32 diff); void Execute_Call(u32 op, u32 diff); + void Execute_CallFast(u32 op, u32 diff); void Execute_Ret(u32 op, u32 diff); void Execute_End(u32 op, u32 diff); @@ -288,6 +290,7 @@ protected: virtual void FinishDeferred() {} void DoBlockTransfer(u32 skipDrawReason); + void DoExecuteCall(u32 target); void AdvanceVerts(u32 vertType, int count, int bytesRead) { if ((vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 06c63f6955..fdca4bf7bd 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -58,7 +58,7 @@ struct GPUgstate { region2, lightingEnable, lightEnable[4], - clipEnable, + depthClampEnable, cullfaceEnable, textureMapEnable, // 0x1E GE_CMD_TEXTUREMAPENABLE fogEnable, @@ -392,8 +392,9 @@ struct GPUgstate { int getRegionX2() const { return (region2 & 0x3FF); } int getRegionY2() const { return (region2 >> 10) & 0x3FF; } + bool isDepthClampEnabled() const { return depthClampEnable & 1; } + // Note that the X1/Y1/Z1 here does not mean the upper-left corner, but half the dimensions. X2/Y2/Z2 are the center. - bool isClippingEnabled() const { return clipEnable & 1; } float getViewportXScale() const { return getFloat24(viewportxscale); } float getViewportYScale() const { return getFloat24(viewportyscale); } float getViewportZScale() const { return getFloat24(viewportzscale); } diff --git a/GPU/GeDisasm.cpp b/GPU/GeDisasm.cpp index d334385691..4faaca6a43 100644 --- a/GPU/GeDisasm.cpp +++ b/GPU/GeDisasm.cpp @@ -297,8 +297,8 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) { } break; - case GE_CMD_CLIPENABLE: - snprintf(buffer, bufsize, "Clip enable: %i", data); + case GE_CMD_DEPTHCLAMPENABLE: + snprintf(buffer, bufsize, "Depth clamp enable: %i", data); break; case GE_CMD_CULLFACEENABLE: diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index 29a50f2054..c4b345d96e 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -137,8 +137,8 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) { } break; - case GE_CMD_CLIPENABLE: - DEBUG_LOG(G3D, "DL Clip Enable: %i (ignoring)", data); + case GE_CMD_DEPTHCLAMPENABLE: + DEBUG_LOG(G3D, "DL Depth Clamp Enable: %i (ignoring)", data); //we always clip, this is opengl break; diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index 2dcb1dd919..851747807d 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -39,6 +39,7 @@ enum { static inline int CalcClipMask(const ClipCoords& v) { int mask = 0; + // This checks `x / w` compared to 1 or -1, skipping the division. if (v.x > v.w) mask |= CLIP_POS_X_BIT; if (v.x < -v.w) mask |= CLIP_NEG_X_BIT; if (v.y > v.w) mask |= CLIP_POS_Y_BIT; @@ -255,11 +256,7 @@ void ProcessLine(VertexData& v0, VertexData& v1) return; } - if (mask && gstate.isClippingEnabled()) { - // discard if any vertex is outside the near clipping plane - if (mask & CLIP_NEG_Z_BIT) - return; - + if (mask) { CLIP_LINE(CLIP_POS_X_BIT, -1, 0, 0, 1); CLIP_LINE(CLIP_NEG_X_BIT, 1, 0, 0, 1); CLIP_LINE(CLIP_POS_Y_BIT, 0, -1, 0, 1); @@ -303,11 +300,7 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2) mask |= CalcClipMask(v1.clippos); mask |= CalcClipMask(v2.clippos); - if (mask && gstate.isClippingEnabled()) { - // discard if any vertex is outside the near clipping plane - if (mask & CLIP_NEG_Z_BIT) - return; - + if (mask) { for (int i = 0; i < 3; i += 3) { int vlist[2][2*6+1]; int *inlist = vlist[0], *outlist = vlist[1]; diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 2852feff0e..219058c362 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -972,7 +972,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4 65535.f) - z = 65535.f; - } - - if (outside_range_flag && (x > 4095.9375f || y > 4095.9375f || x < 0 || y < 0 || z < 0 || z > 65535.f)) + else if (z > 65535.0f) + z = 65535.0f; + else if (outside_range_flag && (x >= SCREEN_BOUND || y >= SCREEN_BOUND || x < 0 || y < 0)) + *outside_range_flag = true; + } else if (outside_range_flag && (x > SCREEN_BOUND || y >= SCREEN_BOUND || x < 0 || y < 0 || z < 0 || z >= DEPTH_BOUND)) { *outside_range_flag = true; + } // 16 = 0xFFFF / 4095.9375 // Round up at 0.625 to the nearest subpixel. diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index feb83cc62b..4abb57df7d 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -57,6 +57,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { bool enableAlphaTest = id.Bit(FS_BIT_ALPHA_TEST); bool alphaTestAgainstZero = id.Bit(FS_BIT_ALPHA_AGAINST_ZERO); + bool testForceToZero = id.Bit(FS_BIT_TEST_DISCARD_TO_ZERO); bool enableColorTest = id.Bit(FS_BIT_COLOR_TEST); bool colorTestAgainstZero = id.Bit(FS_BIT_COLOR_AGAINST_ZERO); bool enableColorDoubling = id.Bit(FS_BIT_COLOR_DOUBLE); @@ -83,7 +84,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { bool isModeClear = id.Bit(FS_BIT_CLEARMODE); const char *shading = doFlatShading ? "flat" : ""; - bool earlyFragmentTests = !enableAlphaTest && !enableColorTest && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); + bool earlyFragmentTests = ((!enableAlphaTest && !enableColorTest) || testForceToZero) && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); if (earlyFragmentTests) { WRITE(p, "layout (early_fragment_tests) in;\n"); @@ -348,27 +349,28 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { // So we have to scale to account for the difference. std::string alphaTestXCoord = "0"; + const char *discardStatement = testForceToZero ? "v.a = 0.0;" : "discard;"; if (enableAlphaTest) { if (alphaTestAgainstZero) { // When testing against 0 (extremely common), we can avoid some math. // 0.002 is approximately half of 1.0 / 255.0. if (alphaTestFunc == GE_COMP_NOTEQUAL || alphaTestFunc == GE_COMP_GREATER) { - WRITE(p, " if (v.a < 0.002) discard;\n"); + WRITE(p, " if (v.a < 0.002) %s\n", discardStatement); } else if (alphaTestFunc != GE_COMP_NEVER) { // Anything else is a test for == 0. Happens sometimes, actually... - WRITE(p, " if (v.a > 0.002) discard;\n"); + WRITE(p, " if (v.a > 0.002) %s\n", discardStatement); } else { // NEVER has been logged as used by games, although it makes little sense - statically failing. // Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here. - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } else { const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; if (alphaTestFuncs[alphaTestFunc][0] != '#') { - WRITE(p, " if ((roundAndScaleTo255i(v.a) & base.alphacolormask.a) %s base.alphacolorref.a) discard;\n", alphaTestFuncs[alphaTestFunc]); + WRITE(p, " if ((roundAndScaleTo255i(v.a) & base.alphacolormask.a) %s base.alphacolorref.a) %s\n", alphaTestFuncs[alphaTestFunc], discardStatement); } else { // This means NEVER. See above. - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } } @@ -379,22 +381,22 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { // Have my doubts that this special case is actually worth it, but whatever. // 0.002 is approximately half of 1.0 / 255.0. if (colorTestFunc == GE_COMP_NOTEQUAL) { - WRITE(p, " if (v.r + v.g + v.b < 0.002) discard;\n"); + WRITE(p, " if (v.r + v.g + v.b < 0.002) %s\n", discardStatement); } else if (colorTestFunc != GE_COMP_NEVER) { // Anything else is a test for == 0. - WRITE(p, " if (v.r + v.g + v.b > 0.002) discard;\n"); + WRITE(p, " if (v.r + v.g + v.b > 0.002) %s\n", discardStatement); } else { // NEVER has been logged as used by games, although it makes little sense - statically failing. // Maybe we could discard the drawcall, but it's pretty rare. Let's just statically discard here. - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } else { const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; if (colorTestFuncs[colorTestFunc][0] != '#') { WRITE(p, " ivec3 v_scaled = roundAndScaleTo255iv(v.rgb);\n"); - WRITE(p, " if ((v_scaled & base.alphacolormask.rgb) %s (base.alphacolorref.rgb & base.alphacolormask.rgb)) discard;\n", colorTestFuncs[colorTestFunc]); + WRITE(p, " if ((v_scaled & base.alphacolormask.rgb) %s (base.alphacolorref.rgb & base.alphacolormask.rgb)) %s\n", colorTestFuncs[colorTestFunc], discardStatement); } else { - WRITE(p, " discard;\n"); + WRITE(p, " %s\n", discardStatement); } } } diff --git a/GPU/Vulkan/StateMappingVulkan.cpp b/GPU/Vulkan/StateMappingVulkan.cpp index 1e78e352ef..68bc711a1d 100644 --- a/GPU/Vulkan/StateMappingVulkan.cpp +++ b/GPU/Vulkan/StateMappingVulkan.cpp @@ -247,7 +247,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag // Set cull bool wantCull = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled(); key.cullMode = wantCull ? (gstate.getCullMode() ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_BACK_BIT) : VK_CULL_MODE_NONE; - key.depthClampEnable = gstate.isClippingEnabled() && gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP); + key.depthClampEnable = gstate.isDepthClampEnabled() && gstate_c.Supports(GPU_SUPPORTS_DEPTH_CLAMP); } } diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index c0b1bc067e..b765af5bcb 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -43,7 +43,7 @@ enum GECommand { GE_CMD_LIGHTENABLE1 = 0x19, GE_CMD_LIGHTENABLE2 = 0x1A, GE_CMD_LIGHTENABLE3 = 0x1B, - GE_CMD_CLIPENABLE = 0x1C, + GE_CMD_DEPTHCLAMPENABLE = 0x1C, GE_CMD_CULLFACEENABLE = 0x1D, GE_CMD_TEXTUREMAPENABLE = 0x1E, GE_CMD_FOGENABLE = 0x1F, diff --git a/README.md b/README.md index 449e3147dc..74617280f1 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,9 @@ Originally released under the GPL 2.0 (and later) in November 2012 Official website: https://www.ppsspp.org/ +Discord: +https://discord.gg/5NJB6dD + No BIOS file required to play, PPSSPP is an "HLE" emulator. Default settings balance good compatibility and speed. To contribute, see [the development page](https://www.ppsspp.org/development.html). Help testing, investigating, or fixing is always welcome. See [the list of issues](https://github.com/hrydgard/ppsspp/issues). diff --git a/UI/DiscordIntegration.cpp b/UI/DiscordIntegration.cpp new file mode 100644 index 0000000000..54ee337e3f --- /dev/null +++ b/UI/DiscordIntegration.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include "ppsspp_config.h" +#include "Common/Log.h" +#include "Core/Config.h" +#include "DiscordIntegration.h" +#include "i18n/i18n.h" + +#if (PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(LINUX)) && !PPSSPP_PLATFORM(ANDROID) + +#define ENABLE_DISCORD + +#else + +// TODO + +#endif + +#ifdef ENABLE_DISCORD +#include "ext/discord-rpc/include/discord_rpc.h" +#endif + +// TODO: Enable on more platforms. Make optional. + +Discord g_Discord; + +static const char *ppsspp_app_id = "423397985041383434"; + +// No context argument? What? +static void handleDiscordError(int errCode, const char *message) { + ERROR_LOG(SYSTEM, "Discord error code %d: '%s'", errCode, message); +} + +Discord::~Discord() { + assert(!initialized_); +} + +bool Discord::IsEnabled() const { + return g_Config.bDiscordPresence; +} + +void Discord::Init() { + assert(IsEnabled()); + assert(!initialized_); + +#ifdef ENABLE_DISCORD + DiscordEventHandlers eventHandlers{}; + eventHandlers.errored = &handleDiscordError; + Discord_Initialize(ppsspp_app_id, &eventHandlers, 0, nullptr); + INFO_LOG(SYSTEM, "Discord connection initialized"); +#endif + + initialized_ = true; +} + +void Discord::Shutdown() { + assert(initialized_); +#ifdef ENABLE_DISCORD + Discord_Shutdown(); +#endif + initialized_ = false; +} + +void Discord::Update() { + if (!IsEnabled()) { + if (initialized_) { + Shutdown(); + } + return; + } else { + if (!initialized_) { + Init(); + } + } + +#ifdef ENABLE_DISCORD +#ifdef DISCORD_DISABLE_IO_THREAD + Discord_UpdateConnection(); +#endif + Discord_RunCallbacks(); +#endif +} + +void Discord::SetPresenceGame(const char *gameTitle) { + if (!IsEnabled()) + return; + + if (!initialized_) { + Init(); + } + +#ifdef ENABLE_DISCORD + I18NCategory *sc = GetI18NCategory("Screen"); + + DiscordRichPresence discordPresence{}; + discordPresence.state = gameTitle; + std::string details = sc->T("Playing"); + discordPresence.details = details.c_str(); + discordPresence.startTimestamp = time(0); +#ifdef GOLD + discordPresence.largeImageKey = "icon_gold_png"; +#else + discordPresence.largeImageKey = "icon_regular_png"; +#endif + Discord_UpdatePresence(&discordPresence); +#endif +} + +void Discord::SetPresenceMenu() { + if (!IsEnabled()) + return; + + if (!initialized_) { + Init(); + } + +#ifdef ENABLE_DISCORD + I18NCategory *sc = GetI18NCategory("Screen"); + + DiscordRichPresence discordPresence{}; + discordPresence.state = sc->T("In menu"); + discordPresence.details = ""; + discordPresence.startTimestamp = time(0); +#ifdef GOLD + discordPresence.largeImageKey = "icon_gold_png"; +#else + discordPresence.largeImageKey = "icon_regular_png"; +#endif + Discord_UpdatePresence(&discordPresence); +#endif +} diff --git a/UI/DiscordIntegration.h b/UI/DiscordIntegration.h new file mode 100644 index 0000000000..cd700d59ea --- /dev/null +++ b/UI/DiscordIntegration.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +// Simple wrapper around the Discord api. + +// All platforms should call it, but we only actually take action on +// platforms where we want it enabled (only PC initially). + +// All you need to call is FrameCallback, Shutdown, and UpdatePresence. + +class Discord { +public: + ~Discord(); + void Update(); // Call every frame or at least regularly. Will initialize if necessary. + void Shutdown(); + + void SetPresenceGame(const char *gameTitle); + void SetPresenceMenu(); + +private: + void Init(); + bool IsEnabled() const; + + bool initialized_ = false; +}; + +extern Discord g_Discord; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6536427ef7..b40af4822c 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -77,6 +77,7 @@ #include "UI/GameSettingsScreen.h" #include "UI/InstallZipScreen.h" #include "UI/ProfilerDraw.h" +#include "UI/DiscordIntegration.h" #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) #include "Windows/MainWindow.h" @@ -186,15 +187,24 @@ void EmuScreen::bootGame(const std::string &filename) { if (!bootAllowStorage(filename)) return; - //pre-emptive loading of game specific config if possible, to get all the settings + I18NCategory *sc = GetI18NCategory("Screen"); + + invalid_ = true; + + // We don't want to boot with the wrong game specific config, so wait until info is ready. std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, filename, 0); - if (info && !info->id.empty()) { + if (!info || info->pending) + return; + + if (!info->id.empty()) { g_Config.loadGameConfig(info->id); // Reset views in case controls are in a different place. RecreateViews(); - } - invalid_ = true; + g_Discord.SetPresenceGame(info->GetTitle().c_str()); + } else { + g_Discord.SetPresenceGame(sc->T("Untitled PSP game")); + } CoreParameter coreParam{}; coreParam.cpuCore = (CPUCore)g_Config.iCpuCore; @@ -333,6 +343,7 @@ EmuScreen::~EmuScreen() { startDumping = false; } #endif + g_Discord.SetPresenceMenu(); } void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) { diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index caa8f396e3..f727aa1363 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -66,10 +66,7 @@ bool GameInfo::Delete() { // Just delete the one file (TODO: handle two-disk games as well somehow). const char *fileToRemove = filePath_.c_str(); File::Delete(fileToRemove); - auto i = std::find(g_Config.recentIsos.begin(), g_Config.recentIsos.end(), fileToRemove); - if (i != g_Config.recentIsos.end()) { - g_Config.recentIsos.erase(i); - } + g_Config.RemoveRecent(filePath_); return true; } case IdentifiedFileType::PSP_PBP_DIRECTORY: @@ -356,8 +353,10 @@ public: } void run() override { - if (!info_->LoadFromPath(gamePath_)) + if (!info_->LoadFromPath(gamePath_)) { + info_->pending = false; return; + } // In case of a remote file, check if it actually exists before locking. if (!info_->GetFileLoader()->Exists()) { info_->pending = false; @@ -384,6 +383,7 @@ public: goto handleELF; } ERROR_LOG(LOADER, "invalid pbp %s\n", pbpLoader->Path().c_str()); + info_->pending = false; info_->working = false; return; } @@ -552,11 +552,13 @@ handleELF: // few files. auto fl = info_->GetFileLoader(); if (!fl) { + info_->pending = false; info_->working = false; return; // Happens with UWP currently, TODO... } BlockDevice *bd = constructBlockDevice(info_->GetFileLoader().get()); if (!bd) { + info_->pending = false; info_->working = false; return; // nothing to do here.. } diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index cf0f8c0ba5..d10584463e 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -354,31 +354,18 @@ bool GameScreen::isRecentGame(const std::string &gamePath) { if (g_Config.iMaxRecent <= 0) return false; + const std::string resolved = File::ResolvePath(gamePath); for (auto it = g_Config.recentIsos.begin(); it != g_Config.recentIsos.end(); ++it) { -#ifdef _WIN32 - if (!strcmpIgnore((*it).c_str(), gamePath.c_str(), "\\","/")) -#else - if (!strcmp((*it).c_str(), gamePath.c_str())) -#endif + const std::string recent = File::ResolvePath(*it); + if (resolved == recent) return true; } return false; } UI::EventReturn GameScreen::OnRemoveFromRecent(UI::EventParams &e) { - if (g_Config.iMaxRecent <= 0) - return UI::EVENT_DONE; - for (auto it = g_Config.recentIsos.begin(); it != g_Config.recentIsos.end(); ++it) { -#ifdef _WIN32 - if (!strcmpIgnore((*it).c_str(), gamePath_.c_str(), "\\","/")) { -#else - if (!strcmp((*it).c_str(), gamePath_.c_str())) { -#endif - g_Config.recentIsos.erase(it); - screenManager()->switchScreen(new MainScreen()); - return UI::EVENT_DONE; - } - } + g_Config.RemoveRecent(gamePath_); + screenManager()->switchScreen(new MainScreen()); return UI::EVENT_DONE; } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f4527ca23b..2541d2d1a7 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -684,6 +684,7 @@ void GameSettingsScreen::CreateViews() { networkingSettings->Add(new Choice(n->T("Adhoc Multiplayer forum")))->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides); networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)"))); + networkingSettings->Add(new CheckBox(&g_Config.bDiscordPresence, n->T("Send Discord Presence information"))); #if !defined(MOBILE_DEVICE) && !defined(USING_QT_UI) networkingSettings->Add(new PopupTextInputChoice(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), "", 255, screenManager())); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index ed0ed1a645..1c9a4a13ff 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1083,17 +1083,20 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, f return false; } + Draw::Texture *texture = nullptr; if (ginfo->pic1.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic1.texture->GetTexture()); + texture = ginfo->pic1.texture->GetTexture(); } else if (ginfo->pic0.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic0.texture->GetTexture()); + texture = ginfo->pic0.texture->GetTexture(); } uint32_t color = whiteAlpha(ease(progress)) & 0xFFc0c0c0; - dc.Draw()->DrawTexRect(dc.GetBounds(), 0,0,1,1, color); - dc.Flush(); - dc.RebindTexture(); - + if (texture) { + dc.GetDrawContext()->BindTexture(0, texture); + dc.Draw()->DrawTexRect(dc.GetBounds(), 0, 0, 1, 1, color); + dc.Flush(); + dc.RebindTexture(); + } return true; } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 0a7673e1ce..0dbc02e4f8 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -98,6 +98,7 @@ #include "UI/TiltEventProcessor.h" #include "UI/BackgroundAudio.h" #include "UI/TextureUtil.h" +#include "UI/DiscordIntegration.h" #if !defined(MOBILE_DEVICE) #include "Common/KeyMap.h" @@ -352,6 +353,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch InitFastMath(cpu_info.bNEON); SetupAudioFormats(); + g_Discord.SetPresenceMenu(); + // Make sure UI state is MENU. ResetUIState(); @@ -1034,6 +1037,8 @@ void NativeUpdate() { g_DownloadManager.Update(); screenManager->update(); + + g_Discord.Update(); } bool NativeIsAtTopLevel() { @@ -1219,6 +1224,8 @@ void NativeShutdown() { net::Shutdown(); + g_Discord.Shutdown(); + delete logger; logger = nullptr; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 31d8f9f966..7e4238b96c 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -82,6 +82,8 @@ static bool FindServer(std::string &resultHost, int &resultPort) { std::string json; result.TakeAll(&json); + using namespace json; + JsonReader reader(json.c_str(), json.size()); if (!reader.ok()) { return false; diff --git a/UI/Store.cpp b/UI/Store.cpp index da2d2f1a4f..58e780dbda 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -391,6 +391,7 @@ void StoreScreen::update() { } void StoreScreen::ParseListing(std::string json) { + using namespace json; JsonReader reader(json.c_str(), json.size()); if (!reader.ok() || !reader.root()) { ELOG("Error parsing JSON from store"); @@ -540,8 +541,8 @@ std::string StoreScreen::GetStoreJsonURL(std::string storePath) const { return path; } -std::string StoreScreen::GetTranslatedString(const JsonGet json, std::string key, const char *fallback) const { - JsonGet dict = json.getDict("en_US"); +std::string StoreScreen::GetTranslatedString(const json::JsonGet json, std::string key, const char *fallback) const { + json::JsonGet dict = json.getDict("en_US"); if (dict && json.hasChild(lang_.c_str(), JSON_OBJECT)) { if (json.getDict(lang_.c_str()).hasChild(key.c_str(), JSON_STRING)) { dict = json.getDict(lang_.c_str()); diff --git a/UI/Store.h b/UI/Store.h index e7786c7c9c..c935bcb042 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -29,7 +29,10 @@ // set game specific settings, etc. // Uses GameInfoCache heavily to implement the functionality. -struct JsonGet; +namespace json { + struct JsonGet; +} + class ProductItemView; enum EntryType { @@ -79,7 +82,7 @@ private: std::vector FilterEntries(); std::string GetStoreJsonURL(std::string storePath) const; - std::string GetTranslatedString(const JsonGet json, std::string key, const char *fallback = nullptr) const; + std::string GetTranslatedString(const json::JsonGet json, std::string key, const char *fallback = nullptr) const; std::shared_ptr listing_; std::shared_ptr image_; diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 929871985e..4d1650482b 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -24,6 +24,7 @@ + @@ -54,6 +55,7 @@ + @@ -80,6 +82,11 @@ + + + {beb0a821-3c7f-410f-a525-63afbc69bf8f} + + {004B8D11-2BE3-4BD9-AB40-2BE04CF2096F} Win32Proj @@ -140,7 +147,6 @@ false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h $(EXTERNAL_COMPILE_OPTIONS) @@ -165,7 +171,6 @@ false MultiThreadedDebug false - ProgramDatabase Common/DbgNew.h $(EXTERNAL_COMPILE_OPTIONS) @@ -189,7 +194,7 @@ USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false - Speed + Size true false MultiThreaded @@ -222,6 +227,8 @@ MultiThreaded true $(EXTERNAL_COMPILE_OPTIONS) + false + Size Windows diff --git a/UI/UI.vcxproj.filters b/UI/UI.vcxproj.filters index 8332d11762..0b5c2dd240 100644 --- a/UI/UI.vcxproj.filters +++ b/UI/UI.vcxproj.filters @@ -70,6 +70,7 @@ Screens + @@ -141,6 +142,7 @@ Screens + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj b/UWP/UI_UWP/UI_UWP.vcxproj index 7fda5b55f9..3b54be0228 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj +++ b/UWP/UI_UWP/UI_UWP.vcxproj @@ -304,6 +304,7 @@ + @@ -337,6 +338,7 @@ + @@ -376,4 +378,4 @@ - \ No newline at end of file + diff --git a/UWP/UI_UWP/UI_UWP.vcxproj.filters b/UWP/UI_UWP/UI_UWP.vcxproj.filters index 2dda8c4b86..b3ee44e3c8 100644 --- a/UWP/UI_UWP/UI_UWP.vcxproj.filters +++ b/UWP/UI_UWP/UI_UWP.vcxproj.filters @@ -7,6 +7,7 @@ + @@ -40,6 +41,7 @@ + @@ -65,4 +67,4 @@ - \ No newline at end of file + diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 494cf3e999..3b3241b947 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -100,7 +100,7 @@ static const TabStateRow stateFlagsRows[] = { { L"Light 1 enable", GE_CMD_LIGHTENABLE1, CMD_FMT_FLAG }, { L"Light 2 enable", GE_CMD_LIGHTENABLE2, CMD_FMT_FLAG }, { L"Light 3 enable", GE_CMD_LIGHTENABLE3, CMD_FMT_FLAG }, - { L"Clip enable", GE_CMD_CLIPENABLE, CMD_FMT_FLAG }, + { L"Depth clamp enable", GE_CMD_DEPTHCLAMPENABLE, CMD_FMT_FLAG }, { L"Cullface enable", GE_CMD_CULLFACEENABLE, CMD_FMT_FLAG }, { L"Texture map enable", GE_CMD_TEXTUREMAPENABLE, CMD_FMT_FLAG }, { L"Fog enable", GE_CMD_FOGENABLE, CMD_FMT_FLAG }, diff --git a/Windows/GEDebugger/TabVertices.cpp b/Windows/GEDebugger/TabVertices.cpp index e878105fdd..f1f7e2f164 100644 --- a/Windows/GEDebugger/TabVertices.cpp +++ b/Windows/GEDebugger/TabVertices.cpp @@ -369,7 +369,7 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { if (row >= MATRIXLIST_ROW_BONE_0_0) { int b = (row - MATRIXLIST_ROW_BONE_0_0) / 3; int r = (row - MATRIXLIST_ROW_BONE_0_0) % 3; - int offset = (row - MATRIXLIST_ROW_BONE_0_0) * 4 + col - 1; + int offset = b * 12 + r + (col - 1) * 3; switch (col) { case MATRIXLIST_COL_NAME: @@ -382,7 +382,7 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { } } else if (row >= MATRIXLIST_ROW_TGEN_0) { int r = row - MATRIXLIST_ROW_TGEN_0; - int offset = r * 4 + col - 1; + int offset = r + (col - 1) * 4; switch (col) { case MATRIXLIST_COL_NAME: @@ -395,7 +395,7 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { } } else if (row >= MATRIXLIST_ROW_PROJ_0) { int r = row - MATRIXLIST_ROW_PROJ_0; - int offset = r * 4 + col - 1; + int offset = r + (col - 1) * 4; switch (col) { case MATRIXLIST_COL_NAME: @@ -408,7 +408,7 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { } } else if (row >= MATRIXLIST_ROW_VIEW_0) { int r = row - MATRIXLIST_ROW_VIEW_0; - int offset = r * 4 + col - 1; + int offset = r + (col - 1) * 3; switch (col) { case MATRIXLIST_COL_NAME: @@ -421,7 +421,7 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { } } else { int r = row - MATRIXLIST_ROW_WORLD_0; - int offset = r * 4 + col - 1; + int offset = r + (col - 1) * 3; switch (col) { case MATRIXLIST_COL_NAME: diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index 42e006efd0..3dd9c8486b 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -494,7 +494,7 @@ void CGEDebugger::UpdatePrimPreview(u32 op, int which) { } if (texPreviewVao == 0) { - glDisableVertexAttribArray(previewProgram->a_position); + glDisableVertexAttribArray(texPreviewProgram->a_position); } secondWindow->End(); diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 83b5286771..848b8cb5fa 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -913,7 +913,7 @@ namespace MainWindow InputDevice::StopPolling(); MainThread_Stop(); coreState = CORE_POWERUP; - ResetUIState(); + UpdateUIState(UISTATE_MENU); MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL); InputDevice::BeginPolling(); break; diff --git a/Windows/PPSSPP.sln b/Windows/PPSSPP.sln index 13abef5350..918e2910d6 100644 --- a/Windows/PPSSPP.sln +++ b/Windows/PPSSPP.sln @@ -77,6 +77,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glslang", "..\ext\glslang.v EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SPIRV-Cross", "..\ext\SPIRV-Cross.vcxproj", "{4328A62C-F1E9-47ED-B816-A1A81DAF4363}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "discord-rpc", "..\ext\discord-rpc-build\discord-rpc.vcxproj", "{BEB0A821-3C7F-410F-A525-63AFBC69BF8F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -105,7 +107,6 @@ Global {F761046E-6C38-4428-A5F1-38391A37BB34}.Debug|Win32.Build.0 = Debug|Win32 {F761046E-6C38-4428-A5F1-38391A37BB34}.Debug|x64.ActiveCfg = Debug|x64 {F761046E-6C38-4428-A5F1-38391A37BB34}.Debug|x64.Build.0 = Debug|x64 - {F761046E-6C38-4428-A5F1-38391A37BB34}.Release|ARM.ActiveCfg = Release|Win32 {F761046E-6C38-4428-A5F1-38391A37BB34}.Release|Win32.ActiveCfg = Release|Win32 {F761046E-6C38-4428-A5F1-38391A37BB34}.Release|Win32.Build.0 = Release|Win32 {F761046E-6C38-4428-A5F1-38391A37BB34}.Release|x64.ActiveCfg = Release|x64 @@ -114,7 +115,6 @@ Global {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Debug|Win32.Build.0 = Debug|Win32 {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Debug|x64.ActiveCfg = Debug|x64 {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Debug|x64.Build.0 = Debug|x64 - {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Release|ARM.ActiveCfg = Release|Win32 {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Release|Win32.ActiveCfg = Release|Win32 {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Release|Win32.Build.0 = Release|Win32 {457F45D2-556F-47BC-A31D-AFF0D15BEAED}.Release|x64.ActiveCfg = Release|x64 @@ -191,8 +191,19 @@ Global {4328A62C-F1E9-47ED-B816-A1A81DAF4363}.Release|Win32.Build.0 = Release|Win32 {4328A62C-F1E9-47ED-B816-A1A81DAF4363}.Release|x64.ActiveCfg = Release|x64 {4328A62C-F1E9-47ED-B816-A1A81DAF4363}.Release|x64.Build.0 = Release|x64 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Debug|Win32.ActiveCfg = Debug|Win32 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Debug|Win32.Build.0 = Debug|Win32 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Debug|x64.ActiveCfg = Debug|x64 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Debug|x64.Build.0 = Debug|x64 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Release|Win32.ActiveCfg = Release|Win32 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Release|Win32.Build.0 = Release|Win32 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Release|x64.ActiveCfg = Release|x64 + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2FD47774-A031-48F4-B645-A49A3140A29B} + EndGlobalSection EndGlobal diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 7fa9fc4525..e7057f6f41 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -125,7 +125,6 @@ Sync Use Level3 - ProgramDatabase ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h;Common/DbgNew.h MultiThreadedDebug @@ -162,7 +161,6 @@ Sync Use Level3 - ProgramDatabase ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h;Common/DbgNew.h MultiThreadedDebug @@ -559,4 +557,4 @@ - + \ No newline at end of file diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 8cfb5cdbb3..80851fdc34 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -311,9 +311,6 @@ Other Platforms\Qt - - Other Platforms\Qt\Debugger - Windows\System @@ -428,4 +425,4 @@ Resource Files - + \ No newline at end of file diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 2b837a0ef2..171ca31310 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -385,6 +385,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/sceSsl.cpp \ $(SRC)/Core/HLE/sceUmd.cpp \ $(SRC)/Core/HLE/sceUsb.cpp \ + $(SRC)/Core/HLE/sceUsbAcc.cpp \ $(SRC)/Core/HLE/sceUsbCam.cpp \ $(SRC)/Core/HLE/sceUsbGps.cpp \ $(SRC)/Core/HLE/sceUtility.cpp \ @@ -485,6 +486,7 @@ LOCAL_SRC_FILES := \ $(SRC)/android/jni/native_audio.cpp \ $(SRC)/android/jni/native-audio-so.cpp \ $(SRC)/UI/BackgroundAudio.cpp \ + $(SRC)/UI/DiscordIntegration.cpp \ $(SRC)/UI/DevScreens.cpp \ $(SRC)/UI/DisplayLayoutEditor.cpp \ $(SRC)/UI/DisplayLayoutScreen.cpp \ diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9206bcd59b..1eb3220d8d 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -871,6 +871,11 @@ void getDesiredBackbufferSize(int &sz_x, int &sz_y) { sz_x = 0; sz_y = 0; } + // Round the size up to the nearest multiple of 4. While this may cause a tiny aspect ratio distortion, + // there's some hope that #11151 is a driver bug that might be worked around this way. If this fixes it, + // it'll be worth trying to round to a multiple of 2 instead. + sz_x = (sz_x + 3) & ~3; + sz_y = (sz_y + 3) & ~3; } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JNIEnv *, jclass, jint xres, jint yres, jint dpi, jfloat refreshRate) { diff --git a/assets/gamecontrollerdb.txt b/assets/gamecontrollerdb.txt index aa97cd20a3..582e756eea 100644 --- a/assets/gamecontrollerdb.txt +++ b/assets/gamecontrollerdb.txt @@ -187,3 +187,4 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b 05000000c82d00002038000000010000,8Bitdo NES30 Pro,platform:Linux,a:b1,b:b0,x:b4,y:b3,back:b10,guide:b2,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4, 030000005e040000a102000000010000,Xbox 360 Wireless Receiver,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpdown:b14,dpleft:b11,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5, 050000004c050000cc09000001000000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +03000000bc2000006412000011010000,BETOP CONTROLLER,a:b2,b:b1,y:b0,x:b3,start:b9,guide:b30,back:b8,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, diff --git a/build.gradle b/build.gradle index b32f870af1..7c2a69bf60 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.3' + classpath 'com.android.tools.build:gradle:3.1.4' } } diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index f506eb6787..ade6594e8f 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -12,3 +12,6 @@ add_subdirectory(glslang) add_subdirectory(snappy) add_subdirectory(udis86) add_subdirectory(SPIRV-Cross-build) +if(NOT IOS) +add_subdirectory(discord-rpc-build) +endif() diff --git a/ext/SPIRV-Cross.vcxproj b/ext/SPIRV-Cross.vcxproj index 1211cdd119..a3dc1386f3 100644 --- a/ext/SPIRV-Cross.vcxproj +++ b/ext/SPIRV-Cross.vcxproj @@ -93,6 +93,7 @@ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) false MultiThreadedDebug + true Windows @@ -107,6 +108,7 @@ _DEBUG;_LIB;%(PreprocessorDefinitions) false MultiThreadedDebug + true Windows @@ -123,6 +125,7 @@ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded false + true Windows @@ -141,6 +144,8 @@ NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded false + true + false Windows diff --git a/ext/discord-rpc b/ext/discord-rpc new file mode 160000 index 0000000000..3d3ae7129d --- /dev/null +++ b/ext/discord-rpc @@ -0,0 +1 @@ +Subproject commit 3d3ae7129d17643bc706da0a2eea85aafd10ab3a diff --git a/ext/discord-rpc-build/CMakeLists.txt b/ext/discord-rpc-build/CMakeLists.txt new file mode 100644 index 0000000000..a63ec8eecf --- /dev/null +++ b/ext/discord-rpc-build/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required (VERSION 3.2.0) +project (DiscordRPC) + +set(SRC_DIR ../discord-rpc/src) + + +# format +set(ALL_SOURCE_FILES + ${SRC_DIR}/backoff.h + ${SRC_DIR}/connection.h + ${SRC_DIR}/msg_queue.h + ${SRC_DIR}/rpc_connection.h + ${SRC_DIR}/serialization.h + ${SRC_DIR}/discord_rpc.cpp + ${SRC_DIR}/rpc_connection.cpp + ${SRC_DIR}/serialization.cpp + ) + +if(APPLE) + set(ALL_SOURCE_FILES ${ALL_SOURCE_FILES} + ${SRC_DIR}/discord_register_osx.m + ) +endif() + +if(WIN32) + set(ALL_SOURCE_FILES ${ALL_SOURCE_FILES} ${SRC_DIR}/connection_win.cpp) + set(ALL_SOURCE_FILES ${ALL_SOURCE_FILES} ${SRC_DIR}/discord_register_win.cpp) +else() + set(ALL_SOURCE_FILES ${ALL_SOURCE_FILES} ${SRC_DIR}/connection_unix.cpp) + set(ALL_SOURCE_FILES ${ALL_SOURCE_FILES} ${SRC_DIR}/discord_register_linux.cpp) +endif() + +add_library(discord-rpc STATIC ${ALL_SOURCE_FILES}) + +target_include_directories(discord-rpc PUBLIC ../discord-rpc/src ../discord-rpc/include ../rapidjson/include) diff --git a/ext/discord-rpc-build/discord-rpc.vcxproj b/ext/discord-rpc-build/discord-rpc.vcxproj new file mode 100644 index 0000000000..771c6e4f7d --- /dev/null +++ b/ext/discord-rpc-build/discord-rpc.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + {BEB0A821-3C7F-410F-A525-63AFBC69BF8F} + Win32Proj + discordrpc + + + + + + StaticLibrary + true + $(DefaultPlatformToolset)_xp + Unicode + + + StaticLibrary + false + $(DefaultPlatformToolset)_xp + Unicode + + + StaticLibrary + true + $(DefaultPlatformToolset)_xp + Unicode + + + StaticLibrary + false + $(DefaultPlatformToolset)_xp + Unicode + + + + + + + + + + + + + + + + + + + + + false + + + true + + + true + + + false + + + + Level3 + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\discord-rpc\include;..\discord-rpc\src;..\rapidjson\include + MultiThreaded + true + false + false + + + Windows + true + true + true + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\discord-rpc\include;..\discord-rpc\src;..\rapidjson\include + MultiThreadedDebug + true + + + Windows + true + + + + + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\discord-rpc\include;..\discord-rpc\src;..\rapidjson\include + MultiThreadedDebug + true + + + Windows + true + + + + + Level3 + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\discord-rpc\include;..\discord-rpc\src;..\rapidjson\include + MultiThreaded + true + false + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/ext/discord-rpc-build/discord-rpc.vcxproj.filters b/ext/discord-rpc-build/discord-rpc.vcxproj.filters new file mode 100644 index 0000000000..a10d90a63d --- /dev/null +++ b/ext/discord-rpc-build/discord-rpc.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ext/glslang b/ext/glslang index 806af25f74..29619b2312 160000 --- a/ext/glslang +++ b/ext/glslang @@ -1 +1 @@ -Subproject commit 806af25f749b4d7cd861eaebc32474b3a7d102c0 +Subproject commit 29619b2312f7bc862221749f3f4d37c3e6a0dee2 diff --git a/ext/glslang.vcxproj b/ext/glslang.vcxproj index 3711148daa..95eb3ab485 100644 --- a/ext/glslang.vcxproj +++ b/ext/glslang.vcxproj @@ -22,8 +22,7 @@ {EDFA2E87-8AC1-4853-95D4-D7594FF81947} Win32Proj glslang - - + 7.0 @@ -36,7 +35,6 @@ StaticLibrary false $(DefaultPlatformToolset)_xp - false Unicode @@ -49,7 +47,6 @@ StaticLibrary false $(DefaultPlatformToolset)_xp - false Unicode @@ -141,6 +138,7 @@ MultiThreaded false true + false Windows diff --git a/ext/libarmips.vcxproj b/ext/libarmips.vcxproj index 688f859d77..4662b65ba3 100644 --- a/ext/libarmips.vcxproj +++ b/ext/libarmips.vcxproj @@ -30,7 +30,8 @@ {129E5E2B-39C1-4D84-96FE-DFD22DBB4A25} Win32Proj libarmips - + + @@ -135,9 +136,9 @@ _CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) MultiThreadedDebug armips - ProgramDatabase + true Windows @@ -155,9 +156,9 @@ MultiThreadedDebug armips false - ProgramDatabase + true Windows @@ -203,6 +204,8 @@ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded armips + true + false Windows @@ -223,6 +226,8 @@ armips false true + true + false Windows diff --git a/ext/libkirk/libkirk.vcxproj b/ext/libkirk/libkirk.vcxproj index f59b9668a9..86f944aea9 100644 --- a/ext/libkirk/libkirk.vcxproj +++ b/ext/libkirk/libkirk.vcxproj @@ -21,7 +21,8 @@ {3BAAE095-E0AB-4B0E-B5DF-CE39C8AE31DE} libkirk - + + @@ -69,13 +70,12 @@ Level3 - Default + EnableFastChecks _CRTDBG_MAP_ALLOC;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS true false false MultiThreadedDebug - ProgramDatabase ../../Common/DbgNew.h @@ -90,7 +90,6 @@ false MultiThreadedDebug false - ProgramDatabase ../../Common/DbgNew.h _CRTDBG_MAP_ALLOC;_USING_V110_SDK71_;%(PreprocessorDefinitions) @@ -130,6 +129,8 @@ false MultiThreaded true + Speed + false true diff --git a/ext/native/json/json_reader.cpp b/ext/native/json/json_reader.cpp index 83fbe94fa4..eaf9d0ae08 100644 --- a/ext/native/json/json_reader.cpp +++ b/ext/native/json/json_reader.cpp @@ -2,6 +2,8 @@ #include "file/vfs.h" #include "json/json_reader.h" +namespace json { + JsonReader::JsonReader(const std::string &filename) { size_t buf_size; buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size); @@ -117,3 +119,5 @@ bool JsonGet::getBool(const char *child_name, bool default_value) const { } return default_value; } + +} // namespace json diff --git a/ext/native/json/json_reader.h b/ext/native/json/json_reader.h index b9e8de5e53..a18915f488 100644 --- a/ext/native/json/json_reader.h +++ b/ext/native/json/json_reader.h @@ -1,9 +1,12 @@ #include #include + #include "base/basictypes.h" #include "base/logging.h" #include "ext/gason/gason.h" +namespace json { + struct JsonGet { JsonGet(const JsonValue &value) : value_(value) { } @@ -83,3 +86,5 @@ private: DISALLOW_COPY_AND_ASSIGN(JsonReader); }; + +} // namespace json diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 7d1c47a620..7440cf731d 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,9 +1,12 @@ #include #include #include + #include "json/json_reader.h" #include "json/json_writer.h" +namespace json { + JsonWriter::JsonWriter(int flags) { pretty_ = (flags & PRETTY) != 0; str_.imbue(std::locale::classic()); @@ -383,3 +386,5 @@ static void json_stringify_array(JsonWriter &writer, const JsonNode *node) { break; } } + +} // namespace json diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index 22c712c856..d82b7151cf 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -13,6 +13,10 @@ #include #include +struct JsonNode; + +namespace json { + class JsonWriter { public: JsonWriter(int flags = NORMAL); @@ -79,5 +83,6 @@ private: bool pretty_; }; -struct JsonNode; std::string json_stringify(const JsonNode *json); + +} // namespace json diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 8fb5a42746..21b09ed6d7 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -82,7 +82,6 @@ Level3 _CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\..\ext;..\..;..\..\dx9sdk\Include;..\..\dx9sdk\Include\DX11;..\zlib;..\ext\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;..\native\ext;%(AdditionalIncludeDirectories); - ProgramDatabase true false false @@ -109,7 +108,6 @@ false MultiThreadedDebug false - ProgramDatabase ../../Common/DbgNew.h @@ -135,6 +133,7 @@ true false MultiThreaded + false Windows @@ -163,6 +162,7 @@ MultiThreaded false true + false Windows diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 1ae55e39f6..d3ad94095e 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -749,6 +749,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } case GLRRenderCommand::UNIFORM4I: { + assert(curProgram); int loc = c.uniform4.loc ? *c.uniform4.loc : -1; if (c.uniform4.name) { loc = curProgram->GetUniformLoc(c.uniform4.name); @@ -774,6 +775,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } case GLRRenderCommand::UNIFORMMATRIX: { + assert(curProgram); int loc = c.uniform4.loc ? *c.uniform4.loc : -1; if (c.uniform4.name) { loc = curProgram->GetUniformLoc(c.uniform4.name); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 75de166d37..c2cdb086cd 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -228,6 +228,9 @@ void GLRenderManager::StopThread() { void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, GLRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil) { assert(insideFrame_); +#ifdef _DEBUG + curProgram_ = nullptr; +#endif // Eliminate dupes. if (steps_.size() && steps_.back()->render.framebuffer == fb && steps_.back()->stepType == GLRStepType::RENDER) { if (color != GLRRenderPassAction::CLEAR && depth != GLRRenderPassAction::CLEAR && stencil != GLRRenderPassAction::CLEAR) { @@ -342,6 +345,7 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec } void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) { + _assert_(texture); GLRStep *step = new GLRStep{ GLRStepType::READBACK_IMAGE }; step->readback_image.texture = texture; step->readback_image.mipLevel = mipLevel; @@ -360,6 +364,10 @@ void GLRenderManager::CopyImageToMemorySync(GLRTexture *texture, int mipLevel, i void GLRenderManager::BeginFrame() { VLOG("BeginFrame"); +#ifdef _DEBUG + curProgram_ = nullptr; +#endif + int curFrame = GetCurFrame(); FrameData &frameData = frameData_[curFrame]; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 16bc24a732..0a09068b90 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -551,6 +551,9 @@ public: _dbg_assert_(G3D, program != nullptr); data.program.program = program; curRenderStep_->commands.push_back(data); +#ifdef _DEBUG + curProgram_ = program; +#endif } void BindPixelPackBuffer(GLRBuffer *buffer) { // Want to support an offset but can't in ES 2.0. We supply an offset when binding the buffers instead. @@ -604,6 +607,9 @@ public: void SetUniformI(GLint *loc, int count, const int *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORM4I }; data.uniform4.loc = loc; data.uniform4.count = count; @@ -613,6 +619,9 @@ public: void SetUniformI1(GLint *loc, int udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORM4I }; data.uniform4.loc = loc; data.uniform4.count = 1; @@ -622,6 +631,9 @@ public: void SetUniformF(GLint *loc, int count, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; data.uniform4.loc = loc; data.uniform4.count = count; @@ -631,6 +643,9 @@ public: void SetUniformF1(GLint *loc, const float udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; data.uniform4.loc = loc; data.uniform4.count = 1; @@ -640,6 +655,9 @@ public: void SetUniformF(const char *name, int count, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORM4F }; data.uniform4.name = name; data.uniform4.count = count; @@ -649,6 +667,9 @@ public: void SetUniformM4x4(GLint *loc, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX }; data.uniformMatrix4.loc = loc; memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); @@ -657,6 +678,9 @@ public: void SetUniformM4x4(const char *name, const float *udata) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); +#ifdef _DEBUG + assert(curProgram_); +#endif GLRRenderData data{ GLRRenderCommand::UNIFORMMATRIX }; data.uniformMatrix4.name = name; memcpy(data.uniformMatrix4.m, udata, sizeof(float) * 16); @@ -937,4 +961,8 @@ private: int targetWidth_ = 0; int targetHeight_ = 0; + +#ifdef _DEBUG + GLRProgram *curProgram_ = nullptr; +#endif }; diff --git a/ext/rapidjson b/ext/rapidjson new file mode 160000 index 0000000000..73063f5002 --- /dev/null +++ b/ext/rapidjson @@ -0,0 +1 @@ +Subproject commit 73063f5002612c6bf64fe24f851cd5cc0d83eef9 diff --git a/ext/snappy/CMakeLists.txt b/ext/snappy/CMakeLists.txt index fb1ed966c4..6ab38acd0e 100644 --- a/ext/snappy/CMakeLists.txt +++ b/ext/snappy/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(Snappy) -if(SNAPPY_FOUND) +if(SNAPPY_FOUND AND USE_SYSTEM_SNAPPY) add_library(system_snappy INTERFACE) add_library(Ext::Snappy ALIAS system_snappy) target_link_libraries(system_snappy INTERFACE Snappy::Snappy) diff --git a/ext/zlib/zlib.vcxproj b/ext/zlib/zlib.vcxproj index a8b71dbed9..cc73897541 100644 --- a/ext/zlib/zlib.vcxproj +++ b/ext/zlib/zlib.vcxproj @@ -106,7 +106,6 @@ StreamingSIMDExtensions2 Fast Default - ProgramDatabase true false false @@ -132,7 +131,6 @@ false false MultiThreadedDebug - ProgramDatabase ../../Common/DbgNew.h @@ -195,4 +193,4 @@ - \ No newline at end of file + diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index 4018293068..e4412a822c 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -23,7 +23,8 @@ Win32Proj Headless PPSSPPHeadless - + + @@ -93,14 +94,13 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native - Default + EnableFastChecks StreamingSIMDExtensions2 Fast true false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h @@ -132,7 +132,6 @@ false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h @@ -155,7 +154,6 @@ Level3 NotUsing - MaxSpeed true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS @@ -190,7 +188,6 @@ Level3 NotUsing - MaxSpeed true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) diff --git a/headless/SDLHeadlessHost.h b/headless/SDLHeadlessHost.h index daa0fe0413..3907f3ab5e 100644 --- a/headless/SDLHeadlessHost.h +++ b/headless/SDLHeadlessHost.h @@ -49,7 +49,6 @@ protected: STOPPED, }; - GraphicsContext *gfx_; volatile RenderThreadState threadState_ = RenderThreadState::IDLE; }; diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 9bfe3d7832..f2172f7238 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -163,7 +163,7 @@ bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsConte void WindowsHeadlessHost::ShutdownGraphics() { gfx_->StopThread(); - while (threadState_ != RenderThreadState::STOPPED) + while (threadState_ != RenderThreadState::STOPPED && threadState_ != RenderThreadState::IDLE) sleep_ms(1); gfx_->Shutdown(); diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 853eee1eb5..907b20cdf6 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -53,6 +53,5 @@ protected: HWND hWnd; HDC hDC; HGLRC hRC; - GraphicsContext *gfx_; volatile RenderThreadState threadState_ = RenderThreadState::IDLE; }; diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 50866689d3..822e4def82 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -15,6 +15,7 @@ #include "Core/HLE/__sceAudio.h" #include "Core/HW/MemoryStick.h" #include "Core/Host.h" +#include "Core/MemMap.h" #include "Core/System.h" #include "Log.h" #include "LogManager.h" @@ -717,12 +718,16 @@ bool retro_unserialize(const void *data, size_t size) { } void *retro_get_memory_data(unsigned id) { - (void)id; + if ( id == RETRO_MEMORY_SYSTEM_RAM ) { + return Memory::GetPointerUnchecked(PSP_GetKernelMemoryBase()) ; + } return NULL; } size_t retro_get_memory_size(unsigned id) { - (void)id; + if ( id == RETRO_MEMORY_SYSTEM_RAM ) { + return Memory::g_MemorySize ; + } return 0; } diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index 215e9a9378..38b7be99b3 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -23,7 +23,8 @@ Win32Proj UnitTests UnitTest - + + @@ -94,7 +95,6 @@ false false MultiThreadedDebug - ProgramDatabase Common/DbgNew.h @@ -117,7 +117,6 @@ false MultiThreadedDebug false - ProgramDatabase Common/DbgNew.h @@ -139,7 +138,7 @@ USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false - Speed + Size true false MultiThreaded @@ -169,6 +168,8 @@ false MultiThreaded true + Size + false Console