mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 02:29:51 +01:00
Merge branch 'master' into TemporaryHackishBranch
# Conflicts: # Core/Compatibility.cpp # Core/Compatibility.h # Core/MIPS/ARM/ArmCompVFPU.cpp # Core/MIPS/ARM64/Arm64CompVFPU.cpp # Core/MIPS/MIPSIntVFPU.cpp # Core/MIPS/MIPSVFPUUtils.cpp # Core/MIPS/MIPSVFPUUtils.h # UI/TouchControlVisibilityScreen.cpp
This commit is contained in:
commit
3012a60b0d
52 files changed
+827
-239
No files matched your search
@@ -110,3 +110,6 @@ debian/ppsspp/
|
||||
|
||||
# YouCompleteMe file
|
||||
.ycm_extra_conf.pyc
|
||||
|
||||
# RenderDoc
|
||||
*.rdc
|
||||
+3
-1
@@ -831,7 +831,7 @@ elseif(TARGET SDL2::SDL2)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} pthread)
|
||||
endif()
|
||||
set(TargetBin PPSSPPSDL)
|
||||
else()
|
||||
elseif(NOT LIBRETRO)
|
||||
message(FATAL_ERROR "Could not find SDL2. Failing.")
|
||||
endif()
|
||||
|
||||
@@ -1647,6 +1647,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/HLE/sceUsbCam.h
|
||||
Core/HLE/sceUsbGps.cpp
|
||||
Core/HLE/sceUsbGps.h
|
||||
Core/HLE/sceUsbMic.cpp
|
||||
Core/HLE/sceUsbMic.h
|
||||
Core/HLE/sceUtility.cpp
|
||||
Core/HLE/sceUtility.h
|
||||
Core/HLE/sceVaudio.cpp
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
#include <cstdint>
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
#include "Common/CommonWindows.h"
|
||||
|
||||
@@ -76,7 +76,8 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "ForceUMDDelay", &flags_.ForceUMDDelay);
|
||||
CheckSetting(iniFile, gameID, "ForceMax60FPS", &flags_.ForceMax60FPS);
|
||||
CheckSetting(iniFile, gameID, "JitInvalidationHack", &flags_.JitInvalidationHack);
|
||||
CheckSetting(iniFile, gameID, "MMULDisableHack", &flags_.MMULDisableHack);
|
||||
CheckSetting(iniFile, gameID, "HideISOFiles", &flags_.HideISOFiles);
|
||||
CheckSetting(iniFile, gameID, "MoreAccurateVMMUL", &flags_.MoreAccurateVMMUL);
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
|
||||
@@ -76,7 +76,8 @@ struct CompatFlags {
|
||||
bool ForceUMDDelay;
|
||||
bool ForceMax60FPS;
|
||||
bool JitInvalidationHack;
|
||||
bool MMULDisableHack;
|
||||
bool HideISOFiles;
|
||||
bool MoreAccurateVMMUL;
|
||||
};
|
||||
|
||||
class IniFile;
|
||||
|
||||
+3
-3
@@ -1209,7 +1209,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
||||
//sadly it won't benefit from all the "version conversion" going on up-above
|
||||
//but these configs shouldn't contain older versions anyhow
|
||||
if (bGameSpecific) {
|
||||
loadGameConfig(gameId_);
|
||||
loadGameConfig(gameId_, gameIdTitle_);
|
||||
}
|
||||
|
||||
CleanRecent();
|
||||
@@ -1531,7 +1531,7 @@ bool Config::saveGameConfig(const std::string &pGameId, const std::string &title
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Config::loadGameConfig(const std::string &pGameId) {
|
||||
bool Config::loadGameConfig(const std::string &pGameId, const std::string &title) {
|
||||
std::string iniFileNameFull = getGameConfigFile(pGameId);
|
||||
|
||||
if (!hasGameConfig(pGameId)) {
|
||||
@@ -1539,7 +1539,7 @@ bool Config::loadGameConfig(const std::string &pGameId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
changeGameSpecific(pGameId);
|
||||
changeGameSpecific(pGameId, title);
|
||||
IniFile iniFile;
|
||||
iniFile.Load(iniFileNameFull);
|
||||
|
||||
|
||||
+1
-1
@@ -430,7 +430,7 @@ public:
|
||||
void changeGameSpecific(const std::string &gameId = "", const std::string &title = "");
|
||||
bool createGameConfig(const std::string &game_id);
|
||||
bool deleteGameConfig(const std::string& pGameId);
|
||||
bool loadGameConfig(const std::string &game_id);
|
||||
bool loadGameConfig(const std::string &game_id, const std::string &title);
|
||||
bool saveGameConfig(const std::string &pGameId, const std::string &title);
|
||||
void unloadGameConfig();
|
||||
std::string getGameConfigFile(const std::string &gameId);
|
||||
|
||||
@@ -374,6 +374,7 @@
|
||||
<ClCompile Include="HLE\KUBridge.cpp" />
|
||||
<ClCompile Include="HLE\sceUsbAcc.cpp" />
|
||||
<ClCompile Include="HLE\sceUsbCam.cpp" />
|
||||
<ClCompile Include="HLE\sceUsbMic.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRAsm.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRCompALU.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRCompBranch.cpp" />
|
||||
@@ -901,6 +902,7 @@
|
||||
<ClInclude Include="HLE\KUBridge.h" />
|
||||
<ClInclude Include="HLE\sceUsbAcc.h" />
|
||||
<ClInclude Include="HLE\sceUsbCam.h" />
|
||||
<ClInclude Include="HLE\sceUsbMic.h" />
|
||||
<ClInclude Include="MIPS\IR\IRFrontend.h" />
|
||||
<ClInclude Include="MIPS\IR\IRInst.h" />
|
||||
<ClInclude Include="MIPS\IR\IRInterpreter.h" />
|
||||
|
||||
@@ -737,6 +737,9 @@
|
||||
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HLE\sceUsbMic.cpp">
|
||||
<Filter>HLE\Libraries</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@@ -1367,6 +1370,9 @@
|
||||
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HLE\sceUsbMic.h">
|
||||
<Filter>HLE\Libraries</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <cstdio>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "Common/Log.h"
|
||||
@@ -28,6 +29,8 @@
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
|
||||
std::atomic<bool> anyMemChecks_(false);
|
||||
|
||||
static std::mutex breakPointsMutex_;
|
||||
std::vector<BreakPoint> CBreakPoints::breakPoints_;
|
||||
u32 CBreakPoints::breakSkipFirstAt_ = 0;
|
||||
@@ -388,6 +391,7 @@ void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, Break
|
||||
check.result = result;
|
||||
|
||||
memChecks_.push_back(check);
|
||||
anyMemChecks_ = true;
|
||||
guard.unlock();
|
||||
Update();
|
||||
}
|
||||
@@ -395,6 +399,7 @@ void CBreakPoints::AddMemCheck(u32 start, u32 end, MemCheckCondition cond, Break
|
||||
{
|
||||
memChecks_[mc].cond = (MemCheckCondition)(memChecks_[mc].cond | cond);
|
||||
memChecks_[mc].result = (BreakAction)(memChecks_[mc].result | result);
|
||||
anyMemChecks_ = true;
|
||||
guard.unlock();
|
||||
Update();
|
||||
}
|
||||
@@ -410,6 +415,7 @@ void CBreakPoints::RemoveMemCheck(u32 start, u32 end)
|
||||
if (mc != INVALID_MEMCHECK)
|
||||
{
|
||||
memChecks_.erase(memChecks_.begin() + mc);
|
||||
anyMemChecks_ = !memChecks_.empty();
|
||||
guard.unlock();
|
||||
Update();
|
||||
}
|
||||
@@ -499,6 +505,8 @@ MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) {
|
||||
|
||||
BreakAction CBreakPoints::ExecMemCheck(u32 address, bool write, int size, u32 pc)
|
||||
{
|
||||
if (!anyMemChecks_)
|
||||
return BREAK_ACTION_IGNORE;
|
||||
std::unique_lock<std::mutex> guard(memCheckMutex_);
|
||||
auto check = GetMemCheckLocked(address, size);
|
||||
if (check) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HW/MemoryStick.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Replay.h"
|
||||
#include "Core/Reporting.h"
|
||||
@@ -848,6 +849,7 @@ std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
|
||||
return ReplayApplyDiskListing(myVector, CoreTiming::GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
bool hideISOFiles = PSP_CoreParameter().compat.flags().HideISOFiles;
|
||||
while (true) {
|
||||
PSPFileInfo entry;
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
@@ -864,10 +866,16 @@ std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
|
||||
else
|
||||
entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
|
||||
entry.name = SimulateVFATBug(ConvertWStringToUTF8(findData.cFileName));
|
||||
|
||||
bool hideFile = false;
|
||||
if (hideISOFiles && (endsWithNoCase(entry.name, ".cso") || endsWithNoCase(entry.name, ".iso"))) {
|
||||
// Workaround for DJ Max Portable, see compat.ini.
|
||||
hideFile = true;
|
||||
}
|
||||
tmFromFiletime(entry.atime, findData.ftLastAccessTime);
|
||||
tmFromFiletime(entry.ctime, findData.ftCreationTime);
|
||||
tmFromFiletime(entry.mtime, findData.ftLastWriteTime);
|
||||
if (!listingRoot || (wcscmp(findData.cFileName, L"..") && wcscmp(findData.cFileName, L".")))
|
||||
if (!hideFile && (!listingRoot || (wcscmp(findData.cFileName, L"..") && wcscmp(findData.cFileName, L"."))))
|
||||
myVector.push_back(entry);
|
||||
|
||||
int retval = FindNextFile(hFind, &findData);
|
||||
@@ -893,6 +901,7 @@ std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
|
||||
return ReplayApplyDiskListing(myVector, CoreTiming::GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
bool hideISOFiles = PSP_CoreParameter().compat.flags().HideISOFiles;
|
||||
while ((dirp = readdir(dp)) != NULL) {
|
||||
PSPFileInfo entry;
|
||||
struct stat s;
|
||||
@@ -905,10 +914,17 @@ std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
|
||||
entry.access = s.st_mode & 0x1FF;
|
||||
entry.name = SimulateVFATBug(dirp->d_name);
|
||||
entry.size = s.st_size;
|
||||
|
||||
bool hideFile = false;
|
||||
if (hideISOFiles && (endsWithNoCase(entry.name, ".cso") || endsWithNoCase(entry.name, ".iso"))) {
|
||||
// Workaround for DJ Max Portable, see compat.ini.
|
||||
hideFile = true;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!listingRoot || (strcmp(dirp->d_name, "..") && strcmp(dirp->d_name, ".")))
|
||||
if (!hideFile && (!listingRoot || (strcmp(dirp->d_name, "..") && strcmp(dirp->d_name, "."))))
|
||||
myVector.push_back(entry);
|
||||
}
|
||||
closedir(dp);
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
#include "sceUsbAcc.h"
|
||||
#include "sceUsbCam.h"
|
||||
#include "sceUsbGps.h"
|
||||
#include "sceUsbMic.h"
|
||||
#include "sceUtility.h"
|
||||
#include "sceVaudio.h"
|
||||
#include "sceMt19937.h"
|
||||
@@ -298,6 +299,7 @@ void RegisterAllModules() {
|
||||
Register_sceNetIfhandle();
|
||||
Register_KUBridge();
|
||||
Register_sceUsbAcc();
|
||||
Register_sceUsbMic();
|
||||
|
||||
// add new modules here.
|
||||
}
|
||||
|
||||
+12
-1
@@ -50,6 +50,17 @@ static int sceUsbCamStopMic() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) {
|
||||
INFO_LOG(HLE, "UNIMPL sceUsbCamReadMicBlocking: size: %d", size);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (Memory::IsValidAddress(bufAddr + i)) {
|
||||
Memory::Write_U8(i & 0xFF, bufAddr + i);
|
||||
}
|
||||
}
|
||||
hleEatMicro(1000000 / micParam.frequency * (size / 2));
|
||||
return size;
|
||||
}
|
||||
|
||||
static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) {
|
||||
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo");
|
||||
if (Memory::IsValidRange(paramAddr, sizeof(videoParam))) {
|
||||
@@ -115,7 +126,7 @@ const HLEFunction sceUsbCam[] =
|
||||
{ 0X2E930264, nullptr, "sceUsbCamSetupMicEx", '?', "" },
|
||||
{ 0X82A64030, &WrapI_V<sceUsbCamStartMic>, "sceUsbCamStartMic", 'i', "" },
|
||||
{ 0X5145868A, &WrapI_V<sceUsbCamStopMic>, "sceUsbCamStopMic", 'i', "" },
|
||||
{ 0X36636925, nullptr, "sceUsbCamReadMicBlocking", '?', "" },
|
||||
{ 0X36636925, &WrapI_UU<sceUsbCamReadMicBlocking>, "sceUsbCamReadMicBlocking", 'i', "xx" },
|
||||
{ 0X3DC0088E, nullptr, "sceUsbCamReadMic", '?', "" },
|
||||
{ 0XB048A67D, nullptr, "sceUsbCamWaitReadMicEnd", '?', "" },
|
||||
{ 0XF8847F60, nullptr, "sceUsbCamPollReadMicEnd", '?', "" },
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2019- 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 <mutex>
|
||||
|
||||
#include "base/NativeApp.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
#include "Core/HLE/sceUsbMic.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
|
||||
static int sceUsbMicInputBlocking(u32 size, u32 samplerate, u32 bufAddr) {
|
||||
INFO_LOG(HLE, "UNIMPL sceUsbMicInputBlocking: size: %d, samplerate: %d", size, samplerate);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (Memory::IsValidAddress(bufAddr + i)) {
|
||||
Memory::Write_U8(i & 0xFF, bufAddr + i);
|
||||
}
|
||||
}
|
||||
hleEatMicro(1000000 / samplerate * (size / 2));
|
||||
return size;
|
||||
}
|
||||
|
||||
const HLEFunction sceUsbMic[] =
|
||||
{
|
||||
{0x06128E42, nullptr, "sceUsbMicPollInputEnd", '?', "" },
|
||||
{0x2E6DCDCD, &WrapI_UUU<sceUsbMicInputBlocking>, "sceUsbMicInputBlocking", 'i', "xxx" },
|
||||
{0x45310F07, nullptr, "sceUsbMicInputInitEx", '?', "" },
|
||||
{0x5F7F368D, nullptr, "sceUsbMicInput", '?', "" },
|
||||
{0x63400E20, nullptr, "sceUsbMicGetInputLength", '?', "" },
|
||||
{0xB8E536EB, nullptr, "sceUsbMicInputInit", '?', "" },
|
||||
{0xF899001C, nullptr, "sceUsbMicWaitInputEnd", '?', "" },
|
||||
};
|
||||
|
||||
void Register_sceUsbMic()
|
||||
{
|
||||
RegisterModule("sceUsbMic", ARRAY_SIZE(sceUsbMic), sceUsbMic);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2019- 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_sceUsbMic();
|
||||
@@ -21,15 +21,16 @@
|
||||
#include <cmath>
|
||||
#include "math/math_util.h"
|
||||
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "Core/MIPS/ARM/ArmJit.h"
|
||||
#include "Core/MIPS/ARM/ArmRegCache.h"
|
||||
@@ -1468,15 +1469,17 @@ namespace MIPSComp
|
||||
}
|
||||
|
||||
void ArmJit::Comp_Vmmul(MIPSOpcode op) {
|
||||
if (PSP_CoreParameter().compat.flags().MMULDisableHack)
|
||||
DISABLE;
|
||||
CONDITIONAL_DISABLE(VFPU_MTX_VMMUL);
|
||||
if (js.HasUnknownPrefix()) {
|
||||
if (!js.HasNoPrefix()) {
|
||||
DISABLE;
|
||||
}
|
||||
NEON_IF_AVAILABLE(CompNEON_Vmmul);
|
||||
|
||||
// TODO: This probably ignores prefixes?
|
||||
if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) {
|
||||
// Fall back to interpreter, which has the accurate implementation.
|
||||
// Later we might do something more optimized here.
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
MatrixSize sz = GetMtxSize(op);
|
||||
int n = GetMatrixSide(sz);
|
||||
|
||||
@@ -857,8 +857,6 @@ void ArmJit::CompNEON_Vmmov(MIPSOpcode op) {
|
||||
}
|
||||
|
||||
void ArmJit::CompNEON_Vmmul(MIPSOpcode op) {
|
||||
if (PSP_CoreParameter().compat.flags().MMULDisableHack)
|
||||
DISABLE;
|
||||
CONDITIONAL_DISABLE(VFPU_MTX_VMMUL);
|
||||
|
||||
MatrixSize msz = GetMtxSize(op);
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
#include <cmath>
|
||||
#include "math/math_util.h"
|
||||
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#include "Core/MIPS/ARM64/Arm64Jit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64RegCache.h"
|
||||
@@ -1215,13 +1215,17 @@ namespace MIPSComp {
|
||||
}
|
||||
|
||||
void Arm64Jit::Comp_Vmmul(MIPSOpcode op) {
|
||||
if (PSP_CoreParameter().compat.flags().MMULDisableHack)
|
||||
DISABLE;
|
||||
CONDITIONAL_DISABLE(VFPU_MTX_VMMUL);
|
||||
if (!js.HasNoPrefix()) {
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) {
|
||||
// Fall back to interpreter, which has the accurate implementation.
|
||||
// Later we might do something more optimized here.
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
MatrixSize sz = GetMtxSize(op);
|
||||
int n = GetMatrixSide(sz);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "math/math_util.h"
|
||||
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
@@ -1238,13 +1239,17 @@ namespace MIPSComp {
|
||||
// This may or may not be a win when using the IR interpreter...
|
||||
// Many more instructions to interpret.
|
||||
void IRFrontend::Comp_Vmmul(MIPSOpcode op) {
|
||||
if (PSP_CoreParameter().compat.flags().MMULDisableHack)
|
||||
DISABLE;
|
||||
CONDITIONAL_DISABLE(VFPU_MTX_VMMUL);
|
||||
if (!js.HasNoPrefix()) {
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) {
|
||||
// Fall back to interpreter, which has the accurate implementation.
|
||||
// Later we might do something more optimized here.
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
// Matrix multiply (weird prefixes)
|
||||
// D[0 .. N, 0 .. M] = S[0 .. N, 0 .. M]' * T[0 .. N, 0 .. M]
|
||||
// Note: Behaves as if it's implemented through a series of vdots.
|
||||
|
||||
+157
-49
@@ -23,9 +23,11 @@
|
||||
|
||||
#include "math/math_util.h"
|
||||
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
@@ -70,6 +72,9 @@
|
||||
#define M_SQRT1_2 0.707106781186547524401f
|
||||
#endif
|
||||
|
||||
static const bool USE_VFPU_DOT = false;
|
||||
static const bool USE_VPFU_SQRT = false;
|
||||
|
||||
union FloatBits {
|
||||
float f[4];
|
||||
u32 u[4];
|
||||
@@ -464,6 +469,8 @@ namespace MIPSInt
|
||||
ReadMatrix(s, sz, vs);
|
||||
ReadMatrix(t, sz, vt);
|
||||
|
||||
// TODO: Always use the more accurate path in interpreter?
|
||||
bool useAccurateDot = USE_VFPU_DOT || PSP_CoreParameter().compat.flags().MoreAccurateVMMUL;
|
||||
for (int a = 0; a < n; a++) {
|
||||
for (int b = 0; b < n; b++) {
|
||||
union { float f; uint32_t u; } sum = { 0.0f };
|
||||
@@ -473,11 +480,23 @@ namespace MIPSInt
|
||||
ApplySwizzleT(&t[a * 4], V_Quad);
|
||||
}
|
||||
|
||||
sum.f = vfpu_dot(&s[b * 4], &t[a * 4]);
|
||||
if (my_isnan(sum.f)) {
|
||||
sum.u = 0x7f800001;
|
||||
} else if ((sum.u & 0x7F800000) == 0) {
|
||||
sum.u &= 0xFF800000;
|
||||
if (useAccurateDot) {
|
||||
sum.f = vfpu_dot(&s[b * 4], &t[a * 4]);
|
||||
if (my_isnan(sum.f)) {
|
||||
sum.u = 0x7f800001;
|
||||
} else if ((sum.u & 0x7F800000) == 0) {
|
||||
sum.u &= 0xFF800000;
|
||||
}
|
||||
} else {
|
||||
if (a == n - 1 && b == n - 1) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
sum.f += s[b * 4 + c] * t[a * 4 + c];
|
||||
}
|
||||
} else {
|
||||
for (int c = 0; c < n; c++) {
|
||||
sum.f += s[b * 4 + c] * t[a * 4 + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d[a * 4 + b] = sum.f;
|
||||
@@ -603,13 +622,13 @@ namespace MIPSInt
|
||||
case 4: if (s[i] <= 0) d[i] = 0; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat0
|
||||
case 5: if (s[i] < -1.0f) d[i] = -1.0f; else {if(s[i] > 1.0f) d[i] = 1.0f; else d[i] = s[i];} break; // vsat1
|
||||
case 16: d[i] = 1.0f / s[i]; break; //vrcp
|
||||
case 17: d[i] = 1.0f / sqrtf(s[i]); break; //vrsq
|
||||
case 17: d[i] = USE_VPFU_SQRT ? vfpu_rsqrt(s[i]) : 1.0f / sqrtf(s[i]); break; //vrsq
|
||||
|
||||
case 18: { d[i] = vfpu_sin(s[i]); } break; //vsin
|
||||
case 19: { d[i] = vfpu_cos(s[i]); } break; //vcos
|
||||
case 20: d[i] = powf(2.0f, s[i]); break; //vexp2
|
||||
case 21: d[i] = logf(s[i])/log(2.0f); break; //vlog2
|
||||
case 22: d[i] = fabsf(sqrtf(s[i])); break; //vsqrt
|
||||
case 22: d[i] = USE_VPFU_SQRT ? vfpu_sqrt(s[i]) : fabsf(sqrtf(s[i])); break; //vsqrt
|
||||
case 23: d[i] = asinf(s[i]) / M_PI_2; break; //vasin
|
||||
case 24: d[i] = -1.0f / s[i]; break; // vnrcp
|
||||
case 26: { d[i] = -vfpu_sin(s[i]); } break; // vnsin
|
||||
@@ -1134,11 +1153,18 @@ namespace MIPSInt
|
||||
ReadVector(t, sz, vt);
|
||||
ApplySwizzleT(t, V_Quad);
|
||||
|
||||
d.f = vfpu_dot(s, t);
|
||||
if (my_isnan(d.f)) {
|
||||
d.u = 0x7f800001;
|
||||
} else if ((d.u & 0x7F800000) == 0) {
|
||||
d.u &= 0xFF800000;
|
||||
if (USE_VFPU_DOT) {
|
||||
d.f = vfpu_dot(s, t);
|
||||
if (my_isnan(d.f)) {
|
||||
d.u = 0x7f800001;
|
||||
} else if ((d.u & 0x7F800000) == 0) {
|
||||
d.u &= 0xFF800000;
|
||||
}
|
||||
} else {
|
||||
d.f = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
d.f += s[i] * t[i];
|
||||
}
|
||||
}
|
||||
|
||||
ApplyPrefixD(&d.f, V_Single);
|
||||
@@ -1177,7 +1203,14 @@ namespace MIPSInt
|
||||
}
|
||||
ApplyPrefixST(s, VFPURewritePrefix(VFPU_CTRL_SPREFIX, sprefixRemove, sprefixAdd), V_Quad);
|
||||
|
||||
float sum = vfpu_dot(s, t);
|
||||
float sum = 0.0f;
|
||||
if (USE_VFPU_DOT) {
|
||||
sum = vfpu_dot(s, t);
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sum += s[i] * t[i];
|
||||
}
|
||||
}
|
||||
d = my_isnan(sum) ? fabsf(sum) : sum;
|
||||
ApplyPrefixD(&d, V_Single);
|
||||
WriteVector(&d, V_Single, vd);
|
||||
@@ -1391,8 +1424,14 @@ namespace MIPSInt
|
||||
u32 tprefixAdd = VFPU_SWIZZLE(1, 0, 0, 0);
|
||||
ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
||||
|
||||
d[0] = s[0] * t[0] - s[1] * t[1];
|
||||
d[0] += s[2] * t[2] + s[3] * t[3];
|
||||
if (USE_VFPU_DOT) {
|
||||
s[1] = -s[1];
|
||||
d[0] = vfpu_dot(s, t);
|
||||
} else {
|
||||
d[0] = s[0] * t[0] - s[1] * t[1];
|
||||
d[0] += s[2] * t[2] + s[3] * t[3];
|
||||
}
|
||||
|
||||
ApplyPrefixD(d, sz);
|
||||
WriteVector(d, V_Single, vd);
|
||||
PC += 4;
|
||||
@@ -1413,7 +1452,14 @@ namespace MIPSInt
|
||||
u32 tprefixAdd = VFPU_MAKE_CONSTANTS(VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE, VFPUConst::ONE);
|
||||
ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
||||
|
||||
d = vfpu_dot(s, t);
|
||||
if (USE_VFPU_DOT) {
|
||||
d = vfpu_dot(s, t);
|
||||
} else {
|
||||
d = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
d += s[i] * t[i];
|
||||
}
|
||||
}
|
||||
ApplyPrefixD(&d, V_Single);
|
||||
WriteVector(&d, V_Single, vd);
|
||||
PC += 4;
|
||||
@@ -1444,7 +1490,14 @@ namespace MIPSInt
|
||||
tprefixAdd = 0;
|
||||
ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
||||
|
||||
d = vfpu_dot(s, t);
|
||||
if (USE_VFPU_DOT) {
|
||||
d = vfpu_dot(s, t);
|
||||
} else {
|
||||
d = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
d += s[i] * t[i];
|
||||
}
|
||||
}
|
||||
ApplyPrefixD(&d, V_Single);
|
||||
WriteVector(&d, V_Single, vd);
|
||||
PC += 4;
|
||||
@@ -1576,24 +1629,36 @@ namespace MIPSInt
|
||||
ReadMatrix(s, msz, vs);
|
||||
ReadVector(t, sz, vt);
|
||||
|
||||
float t2[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i < tn) {
|
||||
t2[i] = t[i];
|
||||
} else if (i == ins) {
|
||||
t2[i] = 1.0f;
|
||||
} else {
|
||||
t2[i] = 0.0f;
|
||||
if (USE_VFPU_DOT) {
|
||||
float t2[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i < tn) {
|
||||
t2[i] = t[i];
|
||||
} else if (i == ins) {
|
||||
t2[i] = 1.0f;
|
||||
} else {
|
||||
t2[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ins; i++) {
|
||||
d.f[i] = vfpu_dot(&s[i * 4], t2);
|
||||
for (int i = 0; i < ins; i++) {
|
||||
d.f[i] = vfpu_dot(&s[i * 4], t2);
|
||||
|
||||
if (my_isnan(d.f[i])) {
|
||||
d.u[i] = 0x7f800001;
|
||||
} else if ((d.u[i] & 0x7F800000) == 0) {
|
||||
d.u[i] &= 0xFF800000;
|
||||
if (my_isnan(d.f[i])) {
|
||||
d.u[i] = 0x7f800001;
|
||||
} else if ((d.u[i] & 0x7F800000) == 0) {
|
||||
d.u[i] &= 0xFF800000;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < ins; i++) {
|
||||
d.f[i] = s[i * 4] * t[0];
|
||||
for (int k = 1; k < tn; k++) {
|
||||
d.f[i] += s[i * 4 + k] * t[k];
|
||||
}
|
||||
if (ins >= n) {
|
||||
d.f[i] += s[i * 4 + ins];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1618,12 +1683,19 @@ namespace MIPSInt
|
||||
ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
||||
|
||||
// Really this is the operation all rows probably use (with constant wiring.)
|
||||
d.f[ins] = vfpu_dot(&s[ins * 4], t);
|
||||
if (USE_VFPU_DOT) {
|
||||
d.f[ins] = vfpu_dot(&s[ins * 4], t);
|
||||
|
||||
if (my_isnan(d.f[ins])) {
|
||||
d.u[ins] = 0x7f800001;
|
||||
} else if ((d.u[ins] & 0x7F800000) == 0) {
|
||||
d.u[ins] &= 0xFF800000;
|
||||
if (my_isnan(d.f[ins])) {
|
||||
d.u[ins] = 0x7f800001;
|
||||
} else if ((d.u[ins] & 0x7F800000) == 0) {
|
||||
d.u[ins] &= 0xFF800000;
|
||||
}
|
||||
} else {
|
||||
d.f[ins] = s[ins * 4] * t[0];
|
||||
for (int k = 1; k < 4; k++) {
|
||||
d.f[ins] += s[ins * 4 + k] * t[k];
|
||||
}
|
||||
}
|
||||
|
||||
// D prefix applies to the last element only.
|
||||
@@ -2029,10 +2101,12 @@ namespace MIPSInt
|
||||
case 8: d.f[i] = s[i] * t[i]; break; //vmul
|
||||
}
|
||||
|
||||
if (my_isnan(d.f[i])) {
|
||||
d.u[i] = 0x7f800001;
|
||||
} else if ((d.u[i] & 0x7F800000) == 0) {
|
||||
d.u[i] &= 0xFF800000;
|
||||
if (USE_VFPU_DOT) {
|
||||
if (my_isnan(d.f[i])) {
|
||||
d.u[i] = (d.u[i] & 0xff800001) | 1;
|
||||
} else if ((d.u[i] & 0x7F800000) == 0) {
|
||||
d.u[i] &= 0xFF800000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2066,27 +2140,61 @@ namespace MIPSInt
|
||||
|
||||
switch (sz) {
|
||||
case V_Triple: // vcrsp.t
|
||||
d[0] = s[1]*t[2] - s[2]*t[1];
|
||||
d[1] = s[2]*t[0] - s[0]*t[2];
|
||||
{
|
||||
if (USE_VFPU_DOT) {
|
||||
float t0[4] = { 0.0f, t[2], -t[1], 0.0f };
|
||||
float t1[4] = { -t[2], 0.0f, t[0], 0.0f };
|
||||
d[0] = vfpu_dot(s, t0);
|
||||
d[1] = vfpu_dot(s, t1);
|
||||
} else {
|
||||
d[0] = s[1] * t[2] - s[2] * t[1];
|
||||
d[1] = s[2] * t[0] - s[0] * t[2];
|
||||
}
|
||||
|
||||
// T prefix forces swizzle and negate, can be used to have weird constants.
|
||||
tprefixAdd = VFPU_SWIZZLE(1, 0, 3, 2) | VFPU_NEGATE(0, 1, 0, 0);
|
||||
ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
||||
ApplySwizzleS(s, V_Quad);
|
||||
d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3];
|
||||
if (USE_VFPU_DOT) {
|
||||
// TODO: But flush any infs to 0? This seems sketchy.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (my_isinf(s[i]))
|
||||
s[i] = 0.0f;
|
||||
if (my_isinf(t[i]))
|
||||
t[i] = 0.0f;
|
||||
}
|
||||
d[2] = vfpu_dot(s, t);
|
||||
} else {
|
||||
d[2] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case V_Quad: // vqmul.q
|
||||
d[0] = s[0]*t[3] + s[1]*t[2] - s[2]*t[1] + s[3]*t[0];
|
||||
d[1] = -s[0]*t[2] + s[1]*t[3] + s[2]*t[0] + s[3]*t[1];
|
||||
d[2] = s[0]*t[1] - s[1]*t[0] + s[2]*t[3] + s[3]*t[2];
|
||||
{
|
||||
if (USE_VFPU_DOT) {
|
||||
float t0[4] = { t[3], t[2], -t[1], t[0] };
|
||||
float t1[4] = { -t[2], t[3], t[0], t[1] };
|
||||
float t2[4] = { t[1], -t[0], t[3], t[2] };
|
||||
d[0] = vfpu_dot(s, t0);
|
||||
d[1] = vfpu_dot(s, t1);
|
||||
d[2] = vfpu_dot(s, t2);
|
||||
} else {
|
||||
d[0] = s[0] * t[3] + s[1] * t[2] - s[2] * t[1] + s[3] * t[0];
|
||||
d[1] = -s[0] * t[2] + s[1] * t[3] + s[2] * t[0] + s[3] * t[1];
|
||||
d[2] = s[0] * t[1] - s[1] * t[0] + s[2] * t[3] + s[3] * t[2];
|
||||
}
|
||||
|
||||
// T prefix forces swizzle and negate, can be used to have weird constants.
|
||||
tprefixAdd = VFPU_SWIZZLE(0, 1, 2, 3) | VFPU_NEGATE(1, 1, 1, 0);
|
||||
ApplyPrefixST(t, VFPURewritePrefix(VFPU_CTRL_TPREFIX, tprefixRemove, tprefixAdd), V_Quad);
|
||||
ApplySwizzleS(s, sz);
|
||||
d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3];
|
||||
if (USE_VFPU_DOT)
|
||||
d[3] = vfpu_dot(s, t);
|
||||
else
|
||||
d[3] = s[0] * t[0] + s[1] * t[1] + s[2] * t[2] + s[3] * t[3];
|
||||
break;
|
||||
}
|
||||
|
||||
case V_Pair:
|
||||
// t swizzles invalid so the multiply is always zero.
|
||||
|
||||
+246
-52
@@ -15,9 +15,12 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/BitScan.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
@@ -243,14 +246,36 @@ void ReadMatrix(float *rd, MatrixSize size, int reg) {
|
||||
default: _assert_msg_(JIT, 0, "%s: Bad matrix size", __FUNCTION__);
|
||||
}
|
||||
|
||||
for (int i = 0; i < side; i++) {
|
||||
for (int j = 0; j < side; j++) {
|
||||
int index = mtx * 4;
|
||||
if (transpose)
|
||||
index += ((row+i)&3) + ((col+j)&3)*32;
|
||||
else
|
||||
index += ((col+j)&3) + ((row+i)&3)*32;
|
||||
rd[j*4 + i] = V(index);
|
||||
// The voffset ordering is now integrated in these formulas,
|
||||
// eliminating a table lookup.
|
||||
const float *v = currentMIPS->v + (size_t)mtx * 16;
|
||||
if (transpose) {
|
||||
if (side == 4 && col == 0 && row == 0) {
|
||||
// Fast path: Simple 4x4 transpose. TODO: Optimize.
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
rd[j * 4 + i] = v[i * 4 + j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < side; j++) {
|
||||
for (int i = 0; i < side; i++) {
|
||||
int index = ((row + i) & 3) * 4 + ((col + j) & 3);
|
||||
rd[j * 4 + i] = v[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (side == 4 && col == 0 && row == 0) {
|
||||
// Fast path
|
||||
memcpy(rd, v, sizeof(float) * 16); // rd[j * 4 + i] = v[j * 4 + i];
|
||||
} else {
|
||||
for (int j = 0; j < side; j++) {
|
||||
for (int i = 0; i < side; i++) {
|
||||
int index = ((col + j) & 3) * 4 + ((row + i) & 3);
|
||||
rd[j * 4 + i] = v[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,16 +300,38 @@ void WriteMatrix(const float *rd, MatrixSize size, int reg) {
|
||||
ERROR_LOG_REPORT(CPU, "Write mask used with vfpu matrix instruction.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < side; i++) {
|
||||
for (int j = 0; j < side; j++) {
|
||||
// Hm, I wonder if this should affect matrices at all.
|
||||
if (j != side -1 || !currentMIPS->VfpuWriteMask(i)) {
|
||||
int index = mtx * 4;
|
||||
if (transpose)
|
||||
index += ((row+i)&3) + ((col+j)&3)*32;
|
||||
else
|
||||
index += ((col+j)&3) + ((row+i)&3)*32;
|
||||
V(index) = rd[j*4+i];
|
||||
// The voffset ordering is now integrated in these formulas,
|
||||
// eliminating a table lookup.
|
||||
float *v = currentMIPS->v + (size_t)mtx * 16;
|
||||
if (transpose) {
|
||||
if (side == 4 && row == 0 && col == 0 && currentMIPS->VfpuWriteMask() == 0x0) {
|
||||
// Fast path: Simple 4x4 transpose. TODO: Optimize.
|
||||
for (int j = 0; j < side; j++) {
|
||||
for (int i = 0; i < side; i++) {
|
||||
v[i * 4 + j] = rd[j * 4 + i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < side; j++) {
|
||||
for (int i = 0; i < side; i++) {
|
||||
if (j != side - 1 || !currentMIPS->VfpuWriteMask(i)) {
|
||||
int index = ((row + i) & 3) * 4 + ((col + j) & 3);
|
||||
v[index] = rd[j * 4 + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (side == 4 && row == 0 && col == 0 && currentMIPS->VfpuWriteMask() == 0x0) {
|
||||
memcpy(v, rd, sizeof(float) * 16); // v[j * 4 + i] = rd[j * 4 + i];
|
||||
} else {
|
||||
for (int j = 0; j < side; j++) {
|
||||
for (int i = 0; i < side; i++) {
|
||||
if (j != side - 1 || !currentMIPS->VfpuWriteMask(i)) {
|
||||
int index = ((col + j) & 3) * 4 + ((row + i) & 3);
|
||||
v[index] = rd[j * 4 + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -612,6 +659,10 @@ static uint32_t get_uexp(uint32_t x) {
|
||||
return (x >> 23) & 0xFF;
|
||||
}
|
||||
|
||||
int32_t get_exp(uint32_t x) {
|
||||
return get_uexp(x) - 127;
|
||||
}
|
||||
|
||||
static int32_t get_mant(uint32_t x) {
|
||||
// Note: this returns the hidden 1.
|
||||
return (x & 0x007FFFFF) | 0x00800000;
|
||||
@@ -626,32 +677,58 @@ float vfpu_dot(float a[4], float b[4]) {
|
||||
union float2int {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} intermed[4];
|
||||
};
|
||||
float2int result;
|
||||
float2int src[2];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
intermed[i].f = a[i] * b[i];
|
||||
}
|
||||
|
||||
uint32_t exps[4];
|
||||
int32_t exps[4];
|
||||
int32_t mants[4];
|
||||
uint32_t max_exp = 0;
|
||||
uint32_t last_inf = 0;
|
||||
int32_t signs[4];
|
||||
int32_t max_exp = 0;
|
||||
int32_t last_inf = -1;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
exps[i] = get_uexp(intermed[i].i);
|
||||
// Preserve extra bits of precision in the mantissa during the add.
|
||||
mants[i] = get_mant(intermed[i].i) << EXTRA_BITS;
|
||||
src[0].f = a[i];
|
||||
src[1].f = b[i];
|
||||
|
||||
int32_t aexp = get_uexp(src[0].i);
|
||||
int32_t bexp = get_uexp(src[1].i);
|
||||
int32_t amant = get_mant(src[0].i) << EXTRA_BITS;
|
||||
int32_t bmant = get_mant(src[1].i) << EXTRA_BITS;
|
||||
|
||||
exps[i] = aexp + bexp - 127;
|
||||
if (aexp == 255) {
|
||||
// INF * 0 = NAN
|
||||
if ((src[0].i & 0x007FFFFF) != 0 || bexp == 0) {
|
||||
result.i = 0x7F800001;
|
||||
return result.f;
|
||||
}
|
||||
mants[i] = get_mant(0) << EXTRA_BITS;
|
||||
exps[i] = 255;
|
||||
} else if (bexp == 255) {
|
||||
if ((src[1].i & 0x007FFFFF) != 0 || aexp == 0) {
|
||||
result.i = 0x7F800001;
|
||||
return result.f;
|
||||
}
|
||||
mants[i] = get_mant(0) << EXTRA_BITS;
|
||||
exps[i] = 255;
|
||||
} else {
|
||||
// TODO: Adjust precision?
|
||||
uint64_t adjust = (uint64_t)amant * (uint64_t)bmant;
|
||||
mants[i] = (adjust >> (23 + EXTRA_BITS)) & 0x7FFFFFFF;
|
||||
}
|
||||
signs[i] = get_sign(src[0].i) ^ get_sign(src[1].i);
|
||||
|
||||
if (exps[i] > max_exp) {
|
||||
max_exp = exps[i];
|
||||
}
|
||||
if (exps[i] == 255) {
|
||||
bool diff_sign = last_inf && get_sign(last_inf) != get_sign(intermed[i].i);
|
||||
bool mant_nan = mants[i] != (0x00800000 << EXTRA_BITS);
|
||||
if (diff_sign || mant_nan) {
|
||||
intermed[0].i = 0x7F800001;
|
||||
return intermed[0].f;
|
||||
if (exps[i] >= 255) {
|
||||
// Infinity minus infinity is not a real number.
|
||||
if (last_inf != -1 && signs[i] != last_inf) {
|
||||
result.i = 0x7F800001;
|
||||
return result.f;
|
||||
}
|
||||
last_inf = intermed[i].i;
|
||||
last_inf = signs[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,9 +738,9 @@ float vfpu_dot(float a[4], float b[4]) {
|
||||
if (exp >= 32) {
|
||||
mants[i] = 0;
|
||||
} else {
|
||||
mants[i] >>= max_exp - exps[i];
|
||||
mants[i] >>= exp;
|
||||
}
|
||||
if (get_sign(intermed[i].i)) {
|
||||
if (signs[i]) {
|
||||
mants[i] = -mants[i];
|
||||
}
|
||||
mant_sum += mants[i];
|
||||
@@ -674,30 +751,147 @@ float vfpu_dot(float a[4], float b[4]) {
|
||||
sign_sum = 0x80000000;
|
||||
mant_sum = -mant_sum;
|
||||
}
|
||||
// Chop off the extra bits.
|
||||
|
||||
// Truncate off the extra bits now. We want to zero them for rounding purposes.
|
||||
mant_sum >>= EXTRA_BITS;
|
||||
|
||||
if (mant_sum == 0 || max_exp == 0) {
|
||||
if (mant_sum == 0 || max_exp <= 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
while (mant_sum < 0x00800000) {
|
||||
mant_sum <<= 1;
|
||||
max_exp -= 1;
|
||||
int8_t shift = (int8_t)clz32_nonzero(mant_sum) - 8;
|
||||
if (shift < 0) {
|
||||
// Round to even if we'd shift away a 0.5.
|
||||
const uint32_t round_bit = 1 << (-shift - 1);
|
||||
if ((mant_sum & round_bit) && (mant_sum & (round_bit << 1))) {
|
||||
mant_sum += round_bit;
|
||||
shift = (int8_t)clz32_nonzero(mant_sum) - 8;
|
||||
} else if ((mant_sum & round_bit) && (mant_sum & (round_bit - 1))) {
|
||||
mant_sum += round_bit;
|
||||
shift = (int8_t)clz32_nonzero(mant_sum) - 8;
|
||||
}
|
||||
mant_sum >>= -shift;
|
||||
max_exp += -shift;
|
||||
} else {
|
||||
mant_sum <<= shift;
|
||||
max_exp -= shift;
|
||||
}
|
||||
while (mant_sum >= 0x01000000) {
|
||||
mant_sum >>= 1;
|
||||
max_exp += 1;
|
||||
}
|
||||
_dbg_assert_(JIT, (mant_sum & 0x00800000) != 0);
|
||||
_dbg_assert_msg_(JIT, (mant_sum & 0x00800000) != 0, "Mantissa wrong: %08x", mant_sum);
|
||||
|
||||
if (max_exp >= 255) {
|
||||
max_exp = 255;
|
||||
mant_sum = 0;
|
||||
} else if (max_exp == 0) {
|
||||
} else if (max_exp <= 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
intermed[0].i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF);
|
||||
return intermed[0].f;
|
||||
result.i = sign_sum | (max_exp << 23) | (mant_sum & 0x007FFFFF);
|
||||
return result.f;
|
||||
}
|
||||
|
||||
// TODO: This is still not completely accurate compared to the PSP's vsqrt.
|
||||
float vfpu_sqrt(float a) {
|
||||
union float2int {
|
||||
uint32_t u;
|
||||
float f;
|
||||
};
|
||||
float2int val;
|
||||
val.f = a;
|
||||
|
||||
if ((val.u & 0xff800000) == 0x7f800000) {
|
||||
if ((val.u & 0x007fffff) != 0) {
|
||||
val.u = 0x7f800001;
|
||||
}
|
||||
return val.f;
|
||||
}
|
||||
if ((val.u & 0x7f800000) == 0) {
|
||||
// Kill any sign.
|
||||
val.u = 0;
|
||||
return val.f;
|
||||
}
|
||||
if (val.u & 0x80000000) {
|
||||
val.u = 0x7f800001;
|
||||
return val.f;
|
||||
}
|
||||
|
||||
int k = get_exp(val.u);
|
||||
uint32_t sp = get_mant(val.u);
|
||||
int less_bits = k & 1;
|
||||
k >>= 1;
|
||||
|
||||
uint32_t z = 0x00C00000 >> less_bits;
|
||||
int64_t halfsp = sp >> 1;
|
||||
halfsp <<= 23 - less_bits;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
z = (z >> 1) + (uint32_t)(halfsp / z);
|
||||
}
|
||||
|
||||
val.u = ((k + 127) << 23) | ((z << less_bits) & 0x007FFFFF);
|
||||
// The lower two bits never end up set on the PSP, it seems like.
|
||||
val.u &= 0xFFFFFFFC;
|
||||
|
||||
return val.f;
|
||||
}
|
||||
|
||||
static inline uint32_t mant_mul(uint32_t a, uint32_t b) {
|
||||
uint64_t m = (uint64_t)a * (uint64_t)b;
|
||||
if (m & 0x007FFFFF) {
|
||||
m += 0x01437000;
|
||||
}
|
||||
return m >> 23;
|
||||
}
|
||||
|
||||
float vfpu_rsqrt(float a) {
|
||||
union float2int {
|
||||
uint32_t u;
|
||||
float f;
|
||||
};
|
||||
float2int val;
|
||||
val.f = a;
|
||||
|
||||
if (val.u == 0x7f800000) {
|
||||
return 0.0f;
|
||||
}
|
||||
if ((val.u & 0x7fffffff) > 0x7f800000) {
|
||||
val.u = (val.u & 0x80000000) | 0x7f800001;
|
||||
return val.f;
|
||||
}
|
||||
if ((val.u & 0x7f800000) == 0) {
|
||||
val.u = (val.u & 0x80000000) | 0x7f800000;
|
||||
return val.f;
|
||||
}
|
||||
if (val.u & 0x80000000) {
|
||||
val.u = 0xff800001;
|
||||
return val.f;
|
||||
}
|
||||
|
||||
int k = get_exp(val.u);
|
||||
uint32_t sp = get_mant(val.u);
|
||||
int less_bits = k & 1;
|
||||
k = -(k >> 1);
|
||||
|
||||
uint32_t z = 0x00800000 >> less_bits;
|
||||
uint32_t halfsp = sp >> (1 + less_bits);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
uint32_t oldz = z;
|
||||
uint32_t zsq = mant_mul(z, z);
|
||||
uint32_t correction = 0x00C00000 - mant_mul(halfsp, zsq);
|
||||
z = mant_mul(z, correction);
|
||||
}
|
||||
|
||||
int8_t shift = (int8_t)clz32_nonzero(z) - 8 + less_bits;
|
||||
if (shift < 1) {
|
||||
z >>= -shift;
|
||||
k += -shift;
|
||||
} else if (shift > 0) {
|
||||
z <<= shift;
|
||||
k -= shift;
|
||||
}
|
||||
|
||||
z >>= less_bits;
|
||||
|
||||
val.u = ((k + 127) << 23) | (z & 0x007FFFFF);
|
||||
val.u &= 0xFFFFFFFC;
|
||||
|
||||
return val.f;
|
||||
}
|
||||
@@ -98,6 +98,8 @@ inline float vfpu_clamp(float v, float min, float max) {
|
||||
}
|
||||
|
||||
float vfpu_dot(float a[4], float b[4]);
|
||||
float vfpu_sqrt(float a);
|
||||
float vfpu_rsqrt(float a);
|
||||
|
||||
#define VFPU_FLOAT16_EXP_MAX 0x1f
|
||||
#define VFPU_SH_FLOAT16_SIGN 15
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
#include "math/math_util.h"
|
||||
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSVFPUUtils.h"
|
||||
@@ -2803,13 +2805,16 @@ void Jit::Comp_VScl(MIPSOpcode op) {
|
||||
}
|
||||
|
||||
void Jit::Comp_Vmmul(MIPSOpcode op) {
|
||||
if (PSP_CoreParameter().compat.flags().MMULDisableHack)
|
||||
DISABLE;
|
||||
CONDITIONAL_DISABLE(VFPU_MTX_VMMUL);
|
||||
|
||||
// TODO: This probably ignores prefixes?
|
||||
if (js.HasUnknownPrefix())
|
||||
if (!js.HasNoPrefix()) {
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
if (PSP_CoreParameter().compat.flags().MoreAccurateVMMUL) {
|
||||
// Fall back to interpreter, which has the accurate implementation.
|
||||
// Later we might do something more optimized here.
|
||||
DISABLE;
|
||||
}
|
||||
|
||||
MatrixSize sz = GetMtxSize(op);
|
||||
VectorSize vsz = GetVectorSize(sz);
|
||||
|
||||
+1
-1
@@ -252,7 +252,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
||||
}
|
||||
|
||||
//in case we didn't go through EmuScreen::boot
|
||||
g_Config.loadGameConfig(id);
|
||||
g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE"));
|
||||
host->SendUIMessage("config_loaded", "");
|
||||
INFO_LOG(LOADER,"Loading %s...", bootpath.c_str());
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ void GameManager::Update() {
|
||||
}
|
||||
}
|
||||
|
||||
void countSlashes(std::string fileName, int *slashLocation, int *slashCount) {
|
||||
static void countSlashes(const std::string &fileName, int *slashLocation, int *slashCount) {
|
||||
*slashCount = 0;
|
||||
int lastSlashLocation = -1;
|
||||
*slashLocation = -1;
|
||||
@@ -206,9 +206,7 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) {
|
||||
isoFileIndex = i;
|
||||
}
|
||||
} else if (zippedName.find("textures.ini") != std::string::npos) {
|
||||
int slashCount = 0;
|
||||
int slashLocation = -1;
|
||||
countSlashes(zippedName, &slashLocation, &slashCount);
|
||||
int slashLocation = (int)zippedName.find_last_of('/');
|
||||
if (stripCharsTexturePack == -1 || slashLocation < stripCharsTexturePack + 1) {
|
||||
stripCharsTexturePack = slashLocation + 1;
|
||||
isTexturePack = true;
|
||||
@@ -499,6 +497,9 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile,
|
||||
for (int i = 0; i < info.numFiles; i++) {
|
||||
const char *fn = zip_get_name(z, i, 0);
|
||||
std::string zippedName = fn;
|
||||
if (zippedName.length() < (size_t)info.stripChars) {
|
||||
continue;
|
||||
}
|
||||
std::string outFilename = dest + zippedName.substr(info.stripChars);
|
||||
bool isDir = *outFilename.rbegin() == '/';
|
||||
if (!isDir && outFilename.find("/") != std::string::npos) {
|
||||
@@ -521,8 +522,8 @@ bool GameManager::InstallMemstickGame(struct zip *z, const std::string &zipfile,
|
||||
for (int i = 0; i < info.numFiles; i++) {
|
||||
const char *fn = zip_get_name(z, i, 0);
|
||||
// Note that we do NOT write files that are not in a directory, to avoid random
|
||||
// README files etc.
|
||||
if (fileAllowed(fn)) {
|
||||
// README files etc. (unless allowRoot is true.)
|
||||
if (fileAllowed(fn) && strlen(fn) > (size_t)info.stripChars) {
|
||||
fn += info.stripChars;
|
||||
std::string outFilename = dest + fn;
|
||||
bool isDir = *outFilename.rbegin() == '/';
|
||||
|
||||
@@ -987,7 +987,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms);
|
||||
BindPostShader(uniforms);
|
||||
DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
|
||||
flags = flags | DRAWTEX_TO_BACKBUFFER;
|
||||
DrawActiveTexture(0, 0, fbo_w, fbo_h, fbo_w, fbo_h, 0.0f, 0.0f, 1.0f, 1.0f, ROTATION_LOCKED_HORIZONTAL, flags);
|
||||
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
@@ -1010,6 +1009,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
|
||||
flags = flags | DRAWTEX_TO_BACKBUFFER;
|
||||
// Fullscreen Image
|
||||
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags);
|
||||
} else {
|
||||
shaderManager_->DirtyLastShader(); // dirty lastShader_ BEFORE drawing
|
||||
@@ -1308,7 +1308,8 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
|
||||
|
||||
// Some games use mismatching bitdepths. But make sure the stride matches.
|
||||
// If it doesn't, generally this means we detected the framebuffer with too large a height.
|
||||
bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->height - dstHeight;
|
||||
// Use bufferHeight in case of buffers that resize up and down often per frame (Valkyrie Profile.)
|
||||
bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->bufferHeight - dstHeight;
|
||||
if (match && vfb_byteStride != byteStride) {
|
||||
// Grand Knights History copies with a mismatching stride but a full line at a time.
|
||||
// Makes it hard to detect the wrong transfers in e.g. God of War.
|
||||
@@ -1338,7 +1339,7 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
|
||||
const u32 byteOffset = srcBasePtr - vfb_address;
|
||||
const u32 byteStride = srcStride * bpp;
|
||||
const u32 yOffset = byteOffset / byteStride;
|
||||
bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->height - srcHeight;
|
||||
bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - srcHeight;
|
||||
if (match && vfb_byteStride != byteStride) {
|
||||
if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) {
|
||||
match = false;
|
||||
|
||||
@@ -848,7 +848,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask,
|
||||
const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y || projPos.z < u_cullRangeMin.z";
|
||||
const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y || projPos.z > u_cullRangeMax.z";
|
||||
WRITE(p, " if (%s || %s) {\n", outMin, outMax);
|
||||
WRITE(p, " outPos.w = u_cullRangeMax.w;\n");
|
||||
WRITE(p, " outPos.xyzw = vec4(u_cullRangeMax.w);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " }\n");
|
||||
}
|
||||
|
||||
@@ -305,8 +305,8 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
|
||||
|
||||
if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// backwards notation, should fix that...
|
||||
Vec3 v(vtx[i].x, vtx[i].y, 0.0f);
|
||||
// backwards notation, should fix that...
|
||||
v = v * g_display_rot_matrix;
|
||||
vtx[i].x = v.x;
|
||||
vtx[i].y = v.y;
|
||||
|
||||
@@ -136,6 +136,7 @@ void GPU_Vulkan::LoadCache(std::string filename) {
|
||||
// it can just memcpy the finished shader binaries out of the pipeline cache file.
|
||||
bool result = shaderManagerVulkan_->LoadCache(f);
|
||||
if (result) {
|
||||
// WARNING: See comment in LoadCache if you are tempted to flip the second parameter to true.
|
||||
result = pipelineManager_->LoadCache(f, false, shaderManagerVulkan_, draw_, drawEngine_.GetPipelineLayout());
|
||||
}
|
||||
fclose(f);
|
||||
@@ -159,6 +160,7 @@ void GPU_Vulkan::SaveCache(std::string filename) {
|
||||
if (!f)
|
||||
return;
|
||||
shaderManagerVulkan_->SaveCache(f);
|
||||
// WARNING: See comment in LoadCache if you are tempted to flip the second parameter to true.
|
||||
pipelineManager_->SaveCache(f, false, shaderManagerVulkan_, draw_);
|
||||
INFO_LOG(G3D, "Saved Vulkan pipeline cache");
|
||||
fclose(f);
|
||||
|
||||
@@ -582,6 +582,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
|
||||
uint32_t size;
|
||||
|
||||
if (saveRawPipelineCache) {
|
||||
// WARNING: See comment in LoadCache before using this path.
|
||||
VkResult result = vkGetPipelineCacheData(vulkan_->GetDevice(), pipelineCache_, &dataSize, nullptr);
|
||||
uint32_t size = (uint32_t)dataSize;
|
||||
if (result != VK_SUCCESS) {
|
||||
@@ -670,6 +671,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
|
||||
|
||||
uint32_t size = 0;
|
||||
if (loadRawPipelineCache) {
|
||||
// WARNING: Do not use this path until after reading and implementing https://zeux.io/2019/07/17/serializing-pipeline-cache/ !
|
||||
bool success = fread(&size, sizeof(size), 1, file) == 1;
|
||||
if (!size || !success) {
|
||||
WARN_LOG(G3D, "Zero-sized Vulkan pipeline cache.");
|
||||
|
||||
@@ -48,9 +48,6 @@ static const char *vulkan_glsl_preamble =
|
||||
// texcoord = 2
|
||||
// fog = 3
|
||||
|
||||
|
||||
|
||||
|
||||
#undef WRITE
|
||||
|
||||
#define WRITE p+=sprintf
|
||||
@@ -637,7 +634,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) {
|
||||
const char *outMin = "projPos.x < base.cullRangeMin.x || projPos.y < base.cullRangeMin.y || projPos.z < base.cullRangeMin.z";
|
||||
const char *outMax = "projPos.x > base.cullRangeMax.x || projPos.y > base.cullRangeMax.y || projPos.z > base.cullRangeMax.z";
|
||||
WRITE(p, " if (%s || %s) {\n", outMin, outMax);
|
||||
WRITE(p, " outPos.w = base.cullRangeMax.w;\n");
|
||||
WRITE(p, " outPos.xyzw = vec4(base.cullRangeMax.w);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " }\n");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/const_map.h"
|
||||
#include <map>
|
||||
|
||||
// TODO: Add any missing keys
|
||||
// TODO: Add any missing keys
|
||||
static const std::map<int, int> KeyMapRawQttoNative = InitConstMap<int, int>
|
||||
(Qt::Key_P, NKCODE_P)
|
||||
(Qt::Key_O, NKCODE_O)
|
||||
+1
-1
@@ -33,7 +33,7 @@ QTM_USE_NAMESPACE
|
||||
#include "thin3d/thin3d.h"
|
||||
#include "base/NativeApp.h"
|
||||
#include "net/resolve.h"
|
||||
#include "base/NKCodeFromQt.h"
|
||||
#include "NKCodeFromQt.h"
|
||||
|
||||
#include "Common/GraphicsContext.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "util/const_map.h"
|
||||
#include <map>
|
||||
|
||||
// TODO: Add any missing keys
|
||||
// TODO: Add any missing keys
|
||||
static const std::map<int, int> KeyMapRawSDLtoNative = InitConstMap<int, int>
|
||||
(SDLK_UNKNOWN, NKCODE_UNKNOWN)
|
||||
(SDLK_p, NKCODE_P)
|
||||
+1
-1
@@ -27,7 +27,7 @@ SDLJoystick *joystick = NULL;
|
||||
#include "input/input_state.h"
|
||||
#include "input/keycodes.h"
|
||||
#include "net/resolve.h"
|
||||
#include "base/NKCodeFromSDL.h"
|
||||
#include "NKCodeFromSDL.h"
|
||||
#include "util/const_map.h"
|
||||
#include "util/text/utf8.h"
|
||||
#include "math/math_util.h"
|
||||
|
||||
+1
-1
@@ -202,7 +202,7 @@ void EmuScreen::bootGame(const std::string &filename) {
|
||||
return;
|
||||
|
||||
if (!info->id.empty()) {
|
||||
g_Config.loadGameConfig(info->id);
|
||||
g_Config.loadGameConfig(info->id, info->GetTitle());
|
||||
// Reset views in case controls are in a different place.
|
||||
RecreateViews();
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ void GameSettingsScreen::CreateViews() {
|
||||
ReloadAllPostShaderInfo();
|
||||
|
||||
if (editThenRestore_) {
|
||||
g_Config.loadGameConfig(gameID_);
|
||||
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
|
||||
g_Config.loadGameConfig(gameID_, info->GetTitle());
|
||||
}
|
||||
|
||||
iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60;
|
||||
@@ -1111,6 +1112,9 @@ void GameSettingsScreen::onFinish(DialogResult result) {
|
||||
Reporting::UpdateConfig();
|
||||
g_Config.Save("GameSettingsScreen::onFinish");
|
||||
if (editThenRestore_) {
|
||||
// In case we didn't have the title yet before, try again.
|
||||
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, 0);
|
||||
g_Config.changeGameSpecific(gameID_, info->GetTitle());
|
||||
g_Config.unloadGameConfig();
|
||||
}
|
||||
|
||||
|
||||
@@ -982,6 +982,7 @@ void NativeRender(GraphicsContext *graphicsContext) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Compensate for rotated display if needed.
|
||||
if (g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
ortho = ortho * g_display_rot_matrix;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ void TouchControlVisibilityScreen::CreateViews() {
|
||||
toggles_.push_back({ "Select", &g_Config.touchSelectKey.show, I_SELECT });
|
||||
toggles_.push_back({ "Dpad", &g_Config.touchDpad.show, -1 });
|
||||
toggles_.push_back({ "Analog Stick", &g_Config.touchAnalogStick.show, -1 });
|
||||
toggles_.push_back({ "Right Analog Stick", &g_Config.touchRightAnalogStick.show, -1 });
|
||||
toggles_.push_back({ "Right Analog Stick\n(not used by most games)", &g_Config.touchRightAnalogStick.show, -1 });
|
||||
toggles_.push_back({ "Unthrottle", &g_Config.touchUnthrottleKey.show, -1 });
|
||||
toggles_.push_back({ "Combo0", &g_Config.touchCombo0.show, I_1 });
|
||||
toggles_.push_back({ "Combo1", &g_Config.touchCombo1.show, I_2 });
|
||||
|
||||
@@ -505,12 +505,6 @@
|
||||
<ClInclude Include="..\..\ext\native\base\NativeApp.h">
|
||||
<Filter>base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\native\base\NKCodeFromQt.h">
|
||||
<Filter>base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\native\base\NKCodeFromSDL.h">
|
||||
<Filter>base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\native\base\stringutil.h">
|
||||
<Filter>base</Filter>
|
||||
</ClInclude>
|
||||
|
||||
+22
-26
@@ -132,7 +132,6 @@ namespace MainWindow
|
||||
// gross hack
|
||||
bool noFocusPause = false; // TOGGLE_PAUSE state to override pause on lost focus
|
||||
bool trapMouse = true; // Handles some special cases(alt+tab, win menu) when game is running and mouse is confined
|
||||
bool mouseScrollUsed = false;
|
||||
|
||||
#define MAX_LOADSTRING 100
|
||||
const TCHAR *szWindowClass = TEXT("PPSSPPWnd");
|
||||
@@ -260,7 +259,6 @@ namespace MainWindow
|
||||
}
|
||||
|
||||
void RelaseMouseWheel() {
|
||||
if (mouseScrollUsed) {
|
||||
// For simplicity release both wheel events
|
||||
KeyInput key;
|
||||
key.deviceId = DEVICE_ID_MOUSE;
|
||||
@@ -269,8 +267,6 @@ namespace MainWindow
|
||||
NativeKey(key);
|
||||
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
|
||||
NativeKey(key);
|
||||
mouseScrollUsed = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleSizeChange(int newSizingType) {
|
||||
@@ -710,27 +706,6 @@ namespace MainWindow
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
int wheelDelta = (short)(wParam >> 16);
|
||||
KeyInput key;
|
||||
key.deviceId = DEVICE_ID_MOUSE;
|
||||
|
||||
if (wheelDelta < 0) {
|
||||
key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
|
||||
wheelDelta = -wheelDelta;
|
||||
} else {
|
||||
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
|
||||
}
|
||||
// There's no separate keyup event for mousewheel events,
|
||||
// so we set mouseScrollUsed here and always release if it's true.
|
||||
key.flags = KEY_DOWN | KEY_HASWHEELDELTA | (wheelDelta << 16);
|
||||
mouseScrollUsed = true;
|
||||
SetTimer(hwndMain, TIMER_WHEELRELEASE, WHEELRELEASE_DELAY_MS, 0);
|
||||
NativeKey(key);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_TOUCH:
|
||||
{
|
||||
touchHandler.handleTouchEvent(hWnd, message, wParam, lParam);
|
||||
@@ -847,6 +822,27 @@ namespace MainWindow
|
||||
}
|
||||
break;
|
||||
|
||||
// Wheel events have to stay in WndProc for compatibility with older Windows(7). See #12156
|
||||
case WM_MOUSEWHEEL:
|
||||
{
|
||||
int wheelDelta = (short)(wParam >> 16);
|
||||
KeyInput key;
|
||||
key.deviceId = DEVICE_ID_MOUSE;
|
||||
|
||||
if (wheelDelta < 0) {
|
||||
key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
|
||||
wheelDelta = -wheelDelta;
|
||||
} else {
|
||||
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
|
||||
}
|
||||
// There's no separate keyup event for mousewheel events,
|
||||
// so we release it with a slight delay.
|
||||
key.flags = KEY_DOWN | KEY_HASWHEELDELTA | (wheelDelta << 16);
|
||||
SetTimer(hwndMain, TIMER_WHEELRELEASE, WHEELRELEASE_DELAY_MS, 0);
|
||||
NativeKey(key);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_TIMER:
|
||||
// Hack: Take the opportunity to also show/hide the mouse cursor in fullscreen mode.
|
||||
switch (wParam) {
|
||||
@@ -858,7 +854,7 @@ namespace MainWindow
|
||||
hideCursor = true;
|
||||
KillTimer(hWnd, TIMER_CURSORMOVEUPDATE);
|
||||
return 0;
|
||||
// Hack: need to release wheel event without waiting for another wheel event.
|
||||
// Hack: need to release wheel event with a delay for games to register it was "pressed down".
|
||||
case TIMER_WHEELRELEASE:
|
||||
RelaseMouseWheel();
|
||||
return 0;
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
#define REFTIMES_PER_SEC (10000000/200)
|
||||
#define REFTIMES_PER_MILLISEC (REFTIMES_PER_SEC / 1000)
|
||||
|
||||
WASAPIAudioBackend::WASAPIAudioBackend() {
|
||||
WASAPIAudioBackend::WASAPIAudioBackend() : threadData_(0) {
|
||||
}
|
||||
|
||||
WASAPIAudioBackend::~WASAPIAudioBackend() {
|
||||
@@ -177,7 +177,7 @@ bool WASAPIAudioBackend::Init(HWND window, StreamCallback callback, int sampleRa
|
||||
// This to be run only on the thread.
|
||||
class WASAPIAudioThread {
|
||||
public:
|
||||
WASAPIAudioThread(volatile int &threadData, int &sampleRate, StreamCallback &callback)
|
||||
WASAPIAudioThread(std::atomic<int> &threadData, int &sampleRate, StreamCallback &callback)
|
||||
: threadData_(threadData), sampleRate_(sampleRate), callback_(callback) {
|
||||
}
|
||||
~WASAPIAudioThread();
|
||||
@@ -190,7 +190,7 @@ private:
|
||||
void ShutdownAudioDevice();
|
||||
bool DetectFormat();
|
||||
|
||||
volatile int &threadData_;
|
||||
std::atomic<int> &threadData_;
|
||||
int &sampleRate_;
|
||||
StreamCallback &callback_;
|
||||
|
||||
@@ -200,7 +200,7 @@ private:
|
||||
CMMNotificationClient *notificationClient_ = nullptr;
|
||||
WAVEFORMATEXTENSIBLE *deviceFormat_ = nullptr;
|
||||
IAudioRenderClient *renderClient_ = nullptr;
|
||||
short *shortBuf_ = nullptr;
|
||||
int16_t *shortBuf_ = nullptr;
|
||||
|
||||
enum class Format {
|
||||
UNKNOWN = 0,
|
||||
@@ -210,7 +210,7 @@ private:
|
||||
|
||||
uint32_t numBufferFrames = 0;
|
||||
Format format_ = Format::UNKNOWN;
|
||||
REFERENCE_TIME actualDuration_;
|
||||
REFERENCE_TIME actualDuration_{};
|
||||
};
|
||||
|
||||
WASAPIAudioThread::~WASAPIAudioThread() {
|
||||
@@ -264,6 +264,7 @@ void WASAPIAudioThread::ShutdownAudioDevice() {
|
||||
bool WASAPIAudioThread::DetectFormat() {
|
||||
// Don't know if PCM16 ever shows up here, the documentation only talks about float... but let's blindly
|
||||
// try to support it :P
|
||||
format_ = Format::UNKNOWN;
|
||||
|
||||
if (deviceFormat_->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
if (!memcmp(&deviceFormat_->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(deviceFormat_->SubFormat))) {
|
||||
@@ -285,10 +286,13 @@ bool WASAPIAudioThread::DetectFormat() {
|
||||
|
||||
delete [] shortBuf_;
|
||||
shortBuf_ = nullptr;
|
||||
if (format_ == Format::UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BYTE *pData;
|
||||
HRESULT hresult = renderClient_->GetBuffer(numBufferFrames, &pData);
|
||||
int numSamples = numBufferFrames * deviceFormat_->Format.nChannels;
|
||||
const int numSamples = numBufferFrames * deviceFormat_->Format.nChannels;
|
||||
if (format_ == Format::IEEE_FLOAT) {
|
||||
memset(pData, 0, sizeof(float) * numSamples);
|
||||
shortBuf_ = new short[numBufferFrames * deviceFormat_->Format.nChannels];
|
||||
@@ -298,15 +302,13 @@ bool WASAPIAudioThread::DetectFormat() {
|
||||
|
||||
hresult = renderClient_->ReleaseBuffer(numBufferFrames, 0);
|
||||
actualDuration_ = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * numBufferFrames / deviceFormat_->Format.nSamplesPerSec);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WASAPIAudioThread::Run() {
|
||||
// Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx
|
||||
|
||||
HRESULT hresult;
|
||||
hresult = CoCreateInstance(CLSID_MMDeviceEnumerator,
|
||||
HRESULT hresult = CoCreateInstance(CLSID_MMDeviceEnumerator,
|
||||
nullptr, /* Object is not created as the part of the aggregate */
|
||||
CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&deviceEnumerator_);
|
||||
if (FAILED(hresult))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "WindowsAudio.h"
|
||||
|
||||
// This should only be included from WindowsAudio.cpp and WASAPIStream.cpp.
|
||||
@@ -20,5 +21,5 @@ private:
|
||||
HANDLE hThread_ = nullptr;
|
||||
StreamCallback callback_ = nullptr;
|
||||
int sampleRate_ = 0;
|
||||
volatile int threadData_ = 0;
|
||||
std::atomic<int> threadData_;
|
||||
};
|
||||
@@ -394,6 +394,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Core/HLE/sceUsbAcc.cpp \
|
||||
$(SRC)/Core/HLE/sceUsbCam.cpp \
|
||||
$(SRC)/Core/HLE/sceUsbGps.cpp \
|
||||
$(SRC)/Core/HLE/sceUsbMic.cpp \
|
||||
$(SRC)/Core/HLE/sceUtility.cpp \
|
||||
$(SRC)/Core/HLE/sceVaudio.cpp \
|
||||
$(SRC)/Core/HLE/scePspNpDrm_user.cpp \
|
||||
|
||||
@@ -645,7 +645,7 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv
|
||||
ILOG("Size change detected (previously %d,%d) - calling NativeResized()", old_w, old_h);
|
||||
NativeResized();
|
||||
} else {
|
||||
ILOG("Size didn't change.");
|
||||
ILOG("NativeApp::backbufferReisze: Size didn't change.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+55
-30
@@ -332,24 +332,6 @@ ULJS00201 = true
|
||||
# Bijin Tokei Portable not even a game, but needs it to load 2 miniatures, they will be low res, but better than black
|
||||
NPJH00055 = true
|
||||
|
||||
[MMULDisableHack]
|
||||
# Force software MMUL to fix Tekken leg shaking
|
||||
# Tekken 5 DR
|
||||
ULJS00048 = true
|
||||
ULUS10139 = true
|
||||
UCES00356 = true
|
||||
ULJS19013 = true
|
||||
NPJH50137 = true
|
||||
|
||||
# Tekken 6
|
||||
ULUS10466 = true
|
||||
ULES01376 = true
|
||||
ULJS00224 = true
|
||||
NPUH10047 = true
|
||||
ULAS42214 = true
|
||||
ULJS19054 = true
|
||||
NPJH50184 = true
|
||||
|
||||
[RequireBufferedRendering]
|
||||
# Warn the user that the game will not work and have issue, if buffered rendering is not enabled.
|
||||
# Midnight Club: LA Remix
|
||||
@@ -513,13 +495,13 @@ NPJH50036 = true
|
||||
|
||||
# Burnout games have problems with this on Mali, and have no use for it
|
||||
# Legends
|
||||
ULES00125 = true
|
||||
ULUS10025 = true
|
||||
ULJM05228 = true
|
||||
NPJH50305 = true
|
||||
ULJM05049 = true
|
||||
ULKS46027 = true
|
||||
ULAS42019 = true
|
||||
#ULES00125 = true
|
||||
#ULUS10025 = true
|
||||
#ULJM05228 = true
|
||||
#NPJH50305 = true
|
||||
#ULJM05049 = true
|
||||
#ULKS46027 = true
|
||||
#ULAS42019 = true
|
||||
|
||||
# Dominator
|
||||
ULUS10236 = true
|
||||
@@ -551,15 +533,17 @@ ULES01376 = true
|
||||
ULUS10466 = true
|
||||
|
||||
[MGS2AcidHack]
|
||||
ULUS10006 = true # Metal Gear Acid
|
||||
ULES00008 = true
|
||||
ULJM05001 = true
|
||||
ULAS42007 = true
|
||||
ULJM08001 = true
|
||||
ULUS10077 = true # Metal Gear Acid 2
|
||||
ULAS42035 = true
|
||||
ULES00284 = true
|
||||
ULJM05047 = true
|
||||
ULKS46065 = true
|
||||
ULJM08001 = true
|
||||
ULJM05001 = true
|
||||
ULAS42007 = true
|
||||
ULUS10006 = true
|
||||
ULUS10077 = true
|
||||
ULJM08011 = true
|
||||
|
||||
[SonicRivalsHack]
|
||||
ULES00622 = true # SR1
|
||||
@@ -735,3 +719,44 @@ ULES00034 = true
|
||||
ULES00035 = true
|
||||
# MTX MotoTrax
|
||||
ULUS10138 = true
|
||||
|
||||
[HideISOFiles]
|
||||
# DJ Max Portable has some crude copy-protection functionality where it looks for ISO/CSO files
|
||||
# in a few directories. Prevent this by hiding the files from the game.
|
||||
# To be sure, catch all versions and remixes of the game that's been seen in reports.
|
||||
# It checks the following directories:
|
||||
# /
|
||||
# /PSP/
|
||||
# /PSP/COMMON
|
||||
# /PSP/GAME
|
||||
|
||||
ULKS46116 = true
|
||||
ULKS46189 = true
|
||||
ULKS46190 = true
|
||||
ULKS46236 = true
|
||||
ULKS46240 = true
|
||||
ULKS46059 = true
|
||||
ULUS10403 = true
|
||||
ULUS10538 = true
|
||||
ULKS46050 = true
|
||||
ULJM05836 = true
|
||||
ULJM46236 = true
|
||||
ULJM06034 = true
|
||||
NPHH00260 = true
|
||||
CF0020046 = true
|
||||
CF0020074 = true
|
||||
NPJH50471 = true
|
||||
ULJM06033 = true
|
||||
NPJH50559 = true
|
||||
NPEH00030 = true
|
||||
|
||||
[MoreAccurateVMMUL]
|
||||
# Fixes leg shaking in Tekken 6. The potential for slowdown in other games is large enough
|
||||
# that we will not generally apply this accurate mode where not needed.
|
||||
ULUS10466 = true
|
||||
ULES01376 = true
|
||||
ULJS00224 = true
|
||||
NPUH10047 = true
|
||||
ULAS42214 = true
|
||||
ULJS19054 = true
|
||||
NPJH50184 = true
|
||||
@@ -193,3 +193,4 @@ bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,b
|
||||
03000000380700008532000010010000,MadCatz Madcatz Fightpad,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:b5,righttrigger:b7,
|
||||
030000000d0f0000ee00000011010000,HORI CO.,LTD. HORIPAD mini4,platform:Linux,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:a3,lefttrigger:b6,righttrigger:b7,
|
||||
030000000d0f0000c100000011010000,HORI CO.,LTD. HORIPAD S,platform:Linux,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:a3,lefttrigger:b6,righttrigger:b7,
|
||||
03000000ad1b000003f5000033050000,Hori Fighting Stick VX,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,-leftx:h0.8,+leftx:h0.2,-lefty:h0.1,+lefty:h0.4,lefttrigger:b6,righttrigger:b7,
|
||||
+1
-1
Submodule assets/lang updated: 03707d64bf...b2d7f53fa0.
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@ endif()
|
||||
set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "let's not build binaries we don't need" FORCE)
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON CACHE BOOL "let's not use exceptions" FORCE)
|
||||
|
||||
# This is really a workaround for an NDK 20 compiler issue (PPSSPP issue #12105), but shouldn't hurt.
|
||||
if(ANDROID)
|
||||
set(ENABLE_HLSL OFF CACHE BOOL "let's not build HLSL support we don't need" FORCE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(glslang)
|
||||
add_subdirectory(snappy)
|
||||
add_subdirectory(udis86)
|
||||
|
||||
@@ -158,7 +158,6 @@ void VulkanQueueRunner::InitBackbufferRenderPass() {
|
||||
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.srcAccessMask = 0;
|
||||
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rp_info.attachmentCount = 2;
|
||||
@@ -380,14 +379,18 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &st
|
||||
// Planned optimizations:
|
||||
// * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes
|
||||
// as early as possible in the frame (Wipeout billboards).
|
||||
// * Merge subsequent render passes to the same target that are interspersed with unrelated draws to other render targets (God of War).
|
||||
|
||||
for (int j = 0; j < (int)steps.size() - 1; j++) {
|
||||
for (int j = 0; j < (int)steps.size(); j++) {
|
||||
if (steps[j]->stepType == VKRStepType::RENDER &&
|
||||
steps[j]->render.framebuffer &&
|
||||
steps[j]->render.finalColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||
// Just leave it at color_optimal.
|
||||
steps[j]->render.finalColorLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < (int)steps.size() - 1; j++) {
|
||||
// Push down empty "Clear/Store" renderpasses, and merge them with the first "Load/Store" to the same framebuffer.
|
||||
// Actually let's just bother with the first one for now. This affects Wipeout Pure.
|
||||
if (steps.size() > 1 && steps[j]->stepType == VKRStepType::RENDER &&
|
||||
@@ -459,11 +462,18 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &st
|
||||
case VKRStepType::RENDER_SKIP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deleting all in one go should be easier on the instruction cache than deleting
|
||||
// them as we go - and easier to debug because we can look backwards in the frame.
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
delete steps[i];
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::ApplyMGSHack(std::vector<VKRStep *> &steps) {
|
||||
// Really need a sane way to express transforms of steps.
|
||||
|
||||
// We want to turn a sequence of copy,render(1),copy,render(1),copy,render(1) to copy,copy,copy,render(n).
|
||||
|
||||
for (int i = 0; i < (int)steps.size() - 3; i++) {
|
||||
@@ -531,6 +541,78 @@ void VulkanQueueRunner::ApplyMGSHack(std::vector<VKRStep *> &steps) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// There's also a post processing effect using depals that's just brutal in some parts
|
||||
// of the game.
|
||||
for (int i = 0; i < (int)steps.size() - 3; i++) {
|
||||
int last = -1;
|
||||
if (!(steps[i]->stepType == VKRStepType::RENDER &&
|
||||
steps[i + 1]->stepType == VKRStepType::RENDER &&
|
||||
steps[i + 2]->stepType == VKRStepType::RENDER &&
|
||||
steps[i]->render.numDraws == 1 &&
|
||||
steps[i + 1]->render.numDraws == 1 &&
|
||||
steps[i + 2]->render.numDraws == 1 &&
|
||||
steps[i]->render.color == VKRRenderPassAction::DONT_CARE &&
|
||||
steps[i + 1]->render.color == VKRRenderPassAction::KEEP &&
|
||||
steps[i + 2]->render.color == VKRRenderPassAction::DONT_CARE))
|
||||
continue;
|
||||
VKRFramebuffer *depalFramebuffer = steps[i]->render.framebuffer;
|
||||
VKRFramebuffer *targetFramebuffer = steps[i + 1]->render.framebuffer;
|
||||
// OK, found the start of a post-process sequence. Let's scan until we find the end.
|
||||
for (int j = i; j < steps.size() - 3; j++) {
|
||||
if (((j - i) & 1) == 0) {
|
||||
// This should be a depal draw.
|
||||
if (steps[j]->render.numDraws != 1)
|
||||
break;
|
||||
if (steps[j]->render.color != VKRRenderPassAction::DONT_CARE)
|
||||
break;
|
||||
if (steps[j]->render.framebuffer != depalFramebuffer)
|
||||
break;
|
||||
last = j;
|
||||
} else {
|
||||
// This should be a target draw.
|
||||
if (steps[j]->render.numDraws != 1)
|
||||
break;
|
||||
if (steps[j]->render.color != VKRRenderPassAction::KEEP)
|
||||
break;
|
||||
if (steps[j]->render.framebuffer != targetFramebuffer)
|
||||
break;
|
||||
last = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (last == -1)
|
||||
continue;
|
||||
|
||||
// Combine the depal renders.
|
||||
for (int j = i + 2; j <= last + 1; j += 2) {
|
||||
for (int k = 0; k < (int)steps[j]->commands.size(); k++) {
|
||||
switch (steps[j]->commands[k].cmd) {
|
||||
case VKRRenderCommand::DRAW:
|
||||
case VKRRenderCommand::DRAW_INDEXED:
|
||||
steps[i]->commands.push_back(steps[j]->commands[k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
steps[j]->stepType = VKRStepType::RENDER_SKIP;
|
||||
}
|
||||
|
||||
// Combine the target renders.
|
||||
for (int j = i + 3; j <= last; j += 2) {
|
||||
for (int k = 0; k < (int)steps[j]->commands.size(); k++) {
|
||||
switch (steps[j]->commands[k].cmd) {
|
||||
case VKRRenderCommand::DRAW:
|
||||
case VKRRenderCommand::DRAW_INDEXED:
|
||||
steps[i + 1]->commands.push_back(steps[j]->commands[k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
steps[j]->stepType = VKRStepType::RENDER_SKIP;
|
||||
}
|
||||
|
||||
// We're done - we only expect one of these sequences per frame.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::ApplySonicHack(std::vector<VKRStep *> &steps) {
|
||||
@@ -715,6 +797,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
VkPipelineStageFlags dstStage{};
|
||||
switch (barrier.oldLayout) {
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
@@ -756,6 +841,22 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
return;
|
||||
}
|
||||
|
||||
if (step.render.framebuffer && step.render.framebuffer->color.layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
barrier.subresourceRange.levelCount = 1;
|
||||
barrier.image = step.render.framebuffer->color.image;
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
// This is supposed to bind a vulkan render pass to the command buffer.
|
||||
PerformBindFramebufferAsRenderTarget(step, cmd);
|
||||
|
||||
@@ -874,6 +975,8 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
int w;
|
||||
int h;
|
||||
if (step.render.framebuffer) {
|
||||
_dbg_assert_(G3D, step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
|
||||
VKRFramebuffer *fb = step.render.framebuffer;
|
||||
framebuf = fb->framebuf;
|
||||
w = fb->width;
|
||||
|
||||
@@ -400,6 +400,8 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// More redundant bind elimination.
|
||||
if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color == VKRRenderPassAction::KEEP && curRenderStep_->render.depth == VKRRenderPassAction::KEEP && curRenderStep_->render.stencil == VKRRenderPassAction::KEEP) {
|
||||
// Can trivially kill the last empty render step.
|
||||
assert(steps_.back() == curRenderStep_);
|
||||
@@ -412,10 +414,9 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
|
||||
}
|
||||
|
||||
VKRStep *step = new VKRStep{ VKRStepType::RENDER };
|
||||
// This is what queues up new passes, and can end previous ones.
|
||||
step->render.framebuffer = fb;
|
||||
step->render.color = color;
|
||||
step->render.depth= depth;
|
||||
step->render.depth = depth;
|
||||
step->render.stencil = stencil;
|
||||
step->render.clearColor = clearColor;
|
||||
step->render.clearDepth = clearDepth;
|
||||
@@ -426,8 +427,13 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
|
||||
steps_.push_back(step);
|
||||
|
||||
curRenderStep_ = step;
|
||||
curWidth_ = fb ? fb->width : vulkan_->GetBackbufferWidth();
|
||||
curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight();
|
||||
if (fb) {
|
||||
curWidth_ = fb->width;
|
||||
curHeight_ = fb->height;
|
||||
} else {
|
||||
curWidth_ = vulkan_->GetBackbufferWidth();
|
||||
curHeight_ = vulkan_->GetBackbufferHeight();
|
||||
}
|
||||
}
|
||||
|
||||
bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) {
|
||||
@@ -923,10 +929,10 @@ void VulkanRenderManager::Submit(int frame, bool triggerFence) {
|
||||
cmdBufs[numCmdBufs++] = frameData.mainCmd;
|
||||
|
||||
VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
if (triggerFence && !frameData.skipSwap) {
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &acquireSemaphore_;
|
||||
VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
||||
submit_info.pWaitDstStageMask = waitStage;
|
||||
}
|
||||
submit_info.commandBufferCount = (uint32_t)numCmdBufs;
|
||||
|
||||
Reference in new issue
Block a user