Merge remote-tracking branch 'upstream/master' into patch-13

This commit is contained in:
raven02 2013-08-14 07:29:56 +08:00
commit fad8b77044
111 changed files with 3022 additions and 1285 deletions

1
.gitignore vendored
View file

@ -17,6 +17,7 @@
*.suo
*.aps
*.exp
*.qdact
Debug
Release
Windows/x64

View file

@ -746,6 +746,7 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/CwCheat.h
Core/HDRemaster.cpp
Core/HDRemaster.h
Core/ThreadEventQueue.h
Core/Debugger/Breakpoints.cpp
Core/Debugger/Breakpoints.h
Core/Debugger/DebugInterface.h
@ -897,6 +898,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/scePauth.h
Core/HW/atrac3plus.cpp
Core/HW/atrac3plus.h
Core/HW/AsyncIOManager.cpp
Core/HW/AsyncIOManager.h
Core/HW/MediaEngine.cpp
Core/HW/MediaEngine.h
Core/HW/MpegDemux.cpp
@ -931,6 +934,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/MIPS/MIPSInt.h
Core/MIPS/MIPSIntVFPU.cpp
Core/MIPS/MIPSIntVFPU.h
Core/MIPS/MIPSStackWalk.cpp
Core/MIPS/MIPSStackWalk.h
Core/MIPS/MIPSTables.cpp
Core/MIPS/MIPSTables.h
Core/MIPS/MIPSVFPUUtils.cpp

View file

@ -951,7 +951,7 @@ extern const VFPEnc VFPOps[16][2] = {
{{ -1, -1}, {0x3B, 0x30}}, // 14: VABSi
};
extern const char *VFPOpNames[16] = {
const char *VFPOpNames[16] = {
"VMLA",
"VNMLA",
"VMLS",

View file

@ -272,8 +272,8 @@ public:
u32 Imm12Mod()
{
// This is a IMM12 with the top four bits being rotation and the
// bottom eight being a IMM. This is for instructions that need to
// This is an IMM12 with the top four bits being rotation and the
// bottom eight being an IMM. This is for instructions that need to
// expand a 8bit IMM to a 32bit value and gives you some rotation as
// well.
// Each rotation rotates to the right by 2 bits

View file

@ -96,7 +96,7 @@ private:
#define CHECK_HEAP_INTEGRITY()
// Alignment
#define GC_ALIGNED16(x) __declspec(align(16)) x
#define MEMORY_ALIGNED16(x) __declspec(align(16)) x
#define GC_ALIGNED32(x) __declspec(align(32)) x
#define GC_ALIGNED64(x) __declspec(align(64)) x
#define GC_ALIGNED128(x) __declspec(align(128)) x
@ -137,7 +137,7 @@ private:
#endif
#define __forceinline inline __attribute__((always_inline))
#define GC_ALIGNED16(x) __attribute__((aligned(16))) x
#define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x
#define GC_ALIGNED32(x) __attribute__((aligned(32))) x
#define GC_ALIGNED64(x) __attribute__((aligned(64))) x
#define GC_ALIGNED128(x) __attribute__((aligned(128))) x

View file

@ -424,7 +424,9 @@ const KeyMap_IntStrPair axis_names[] = {
{JOYSTICK_AXIS_GAS, "Gas"},
{JOYSTICK_AXIS_BRAKE, "Brake"},
{JOYSTICK_AXIS_DISTANCE, "Distance"},
{JOYSTICK_AXIS_TILT, "Tilt"}
{JOYSTICK_AXIS_TILT, "Tilt"},
{JOYSTICK_AXIS_MOUSE_REL_X, "MouseDX"},
{JOYSTICK_AXIS_MOUSE_REL_Y, "MouseDY"},
};
static std::string unknown_key_name = "??";

View file

@ -15,6 +15,7 @@ set(SRCS
MIPS/MIPSDisVFPU.cpp
MIPS/MIPSInt.cpp
MIPS/MIPSIntVFPU.cpp
MIPS/MIPSStackWalk.cpp
MIPS/MIPSTables.cpp
MIPS/MIPSVFPUUtils.cpp
MIPS/JitCommon/JitCommon.cpp
@ -74,6 +75,7 @@ set(SRCS
HLE/sceParseHttp.cpp
HLE/sceVaudio.cpp
HLE/sceAudiocodec.cpp
HW/AsyncIOManager.cpp
HW/MemoryStick.cpp
HW/MediaEngine.cpp
HW/SasAudio.cpp

View file

@ -89,6 +89,8 @@ void Config::Load(const char *iniFileName)
#else
cpu->Get("Jit", &bJit, true);
#endif
cpu->Get("SeparateCPUThread", &bSeparateCPUThread, false);
cpu->Get("SeparateIOThread", &bSeparateIOThread, false);
cpu->Get("FastMemory", &bFastMemory, false);
cpu->Get("CPUSpeed", &iLockedCPUSpeed, false);
@ -99,7 +101,14 @@ void Config::Load(const char *iniFileName)
#else
graphics->Get("ResolutionScale", &iWindowZoom, 1);
#endif
graphics->Get("RenderingMode", &iRenderingMode, 1); // default is buffered rendering mode
graphics->Get("RenderingMode", &iRenderingMode,
// Many ARMv6 devices have serious problems with buffered rendering.
#if defined(ARM) && !defined(ARMV7)
0
#else
1
#endif
); // default is buffered rendering mode
graphics->Get("HardwareTransform", &bHardwareTransform, true);
graphics->Get("TextureFiltering", &iTexFiltering, 1);
graphics->Get("SSAA", &bAntiAliasing, 0);
@ -137,7 +146,7 @@ void Config::Load(const char *iniFileName)
sound->Get("VolumeSFX", &iSFXVolume, 7);
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Get("ShowStick", &bShowAnalogStick, false);
control->Get("ShowAnalogStick", &bShowAnalogStick, true);
#ifdef BLACKBERRY
control->Get("ShowTouchControls", &bShowTouchControls, pixel_xres != pixel_yres);
#elif defined(USING_GLES2)
@ -226,6 +235,8 @@ void Config::Save()
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Set("Jit", bJit);
cpu->Set("SeparateCPUThread", bSeparateCPUThread);
cpu->Set("SeparateIOThread", bSeparateIOThread);
cpu->Set("FastMemory", bFastMemory);
cpu->Set("CPUSpeed", iLockedCPUSpeed);

View file

@ -56,6 +56,9 @@ public:
bool bIgnoreBadMemAccess;
bool bFastMemory;
bool bJit;
// Definitely cannot be changed while game is running.
bool bSeparateCPUThread;
bool bSeparateIOThread;
int iLockedCPUSpeed;
bool bAutoSaveSymbolMap;
std::string sReportHost;

View file

@ -251,6 +251,7 @@
<ClCompile Include="HW\MpegDemux.cpp" />
<ClCompile Include="HW\OMAConvert.cpp" />
<ClCompile Include="HW\SasAudio.cpp" />
<ClCompile Include="HW\AsyncIOManager.cpp" />
<ClCompile Include="Loaders.cpp" />
<ClCompile Include="MemMap.cpp" />
<ClCompile Include="MemmapFunctions.cpp" />
@ -335,6 +336,7 @@
<ClCompile Include="PSPMixer.cpp" />
<ClCompile Include="Reporting.cpp" />
<ClCompile Include="SaveState.cpp" />
<ClCompile Include="MIPS\MIPSStackWalk.cpp" />
<ClCompile Include="System.cpp" />
<ClCompile Include="Util\BlockAllocator.cpp" />
<ClCompile Include="Util\PPGeDraw.cpp" />
@ -437,6 +439,7 @@
<ClInclude Include="HW\OMAConvert.h" />
<ClInclude Include="HW\SasAudio.h" />
<ClInclude Include="HW\MemoryStick.h" />
<ClInclude Include="HW\AsyncIOManager.h" />
<ClInclude Include="Loaders.h" />
<ClInclude Include="MemMap.h" />
<ClInclude Include="MIPS\ARM\ArmAsm.h">
@ -485,7 +488,9 @@
<ClInclude Include="PSPMixer.h" />
<ClInclude Include="Reporting.h" />
<ClInclude Include="SaveState.h" />
<ClInclude Include="MIPS\MIPSStackWalk.h" />
<ClInclude Include="System.h" />
<ClInclude Include="ThreadEventQueue.h" />
<ClInclude Include="Util\BlockAllocator.h" />
<ClInclude Include="Util\PPGeDraw.h" />
<ClInclude Include="Util\ppge_atlas.h" />

View file

@ -451,6 +451,12 @@
<ClCompile Include="FileSystems\VirtualDiscFileSystem.cpp">
<Filter>FileSystems</Filter>
</ClCompile>
<ClCompile Include="HW\AsyncIOManager.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="MIPS\MIPSStackWalk.cpp">
<Filter>MIPS</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -837,6 +843,15 @@
<ClInclude Include="FileSystems\VirtualDiscFileSystem.h">
<Filter>FileSystems</Filter>
</ClInclude>
<ClInclude Include="ThreadEventQueue.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="HW\AsyncIOManager.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="MIPS\MIPSStackWalk.h">
<Filter>MIPS</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View file

@ -40,6 +40,7 @@ struct CoreParameter
std::string fileToStart;
std::string mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background.
std::string errorString;
bool startPaused;
bool disableG3Dlog;

View file

@ -74,7 +74,7 @@ volatile u32 hasTsEvents = false;
// as we can already reach that structure through a register.
int slicelength;
s64 globalTimer;
MEMORY_ALIGNED16(s64) globalTimer;
s64 idledCycles;
static std::recursive_mutex externalEventSection;

View file

@ -52,7 +52,8 @@ static u32 ComputeHash(u32 start, u32 size)
void SymbolMap::SortSymbols()
{
std::sort(entries.begin(), entries.end());
lock_guard guard(lock_);
std::sort(entries.begin(), entries.end());
}
void SymbolMap::AnalyzeBackwards()
@ -104,6 +105,7 @@ void SymbolMap::AnalyzeBackwards()
void SymbolMap::ResetSymbolMap()
{
lock_guard guard(lock_);
#ifdef BWLINKS
for (int i=0; i<numEntries; i++)
{
@ -118,6 +120,7 @@ void SymbolMap::ResetSymbolMap()
void SymbolMap::AddSymbol(const char *symbolname, unsigned int vaddress, size_t size, SymbolType st)
{
lock_guard guard(lock_);
MapEntry e;
strncpy(e.name, symbolname, 127);
e.name[127] = '\0';
@ -134,6 +137,7 @@ void SymbolMap::AddSymbol(const char *symbolname, unsigned int vaddress, size_t
bool SymbolMap::LoadSymbolMap(const char *filename)
{
lock_guard guard(lock_);
SymbolMap::ResetSymbolMap();
FILE *f = fopen(filename,"r");
if (!f)
@ -148,7 +152,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
while (!feof(f))
{
char line[512],temp[256];
char line[512], temp[256] = {0};
char *p = fgets(line,512,f);
if(p == NULL)
break;
@ -207,6 +211,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
void SymbolMap::SaveSymbolMap(const char *filename) const
{
lock_guard guard(lock_);
FILE *f = fopen(filename,"w");
if (!f)
return;
@ -221,13 +226,14 @@ void SymbolMap::SaveSymbolMap(const char *filename) const
bool SymbolMap::LoadNocashSym(const char *filename)
{
lock_guard guard(lock_);
FILE *f = fopen(filename,"r");
if (!f)
return false;
while (!feof(f))
{
char line[256],value[256];
char line[256], value[256] = {0};
char *p = fgets(line,256,f);
if(p == NULL)
break;
@ -389,6 +395,8 @@ void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symmask) const
}
}
lock_guard guard(lock_);
SendMessage(listbox, WM_SETREDRAW, FALSE, 0);
SendMessage(listbox, LB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30);
for (auto it = entries.begin(), end = entries.end(); it != end; ++it)
@ -427,11 +435,13 @@ void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask) const
//ListBox_AddString(listbox,"(0x80002000)");
//ListBox_SetItemData(listbox,1,0x80002000);
lock_guard guard(lock_);
SendMessage(listbox, WM_SETREDRAW, FALSE, 0);
SendMessage(listbox, CB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30);
for (auto it = entries.begin(), end = entries.end(); it != end; ++it)
for (size_t i = 0, end = entries.size(); i < end; ++i)
{
const MapEntry &entry = *it;
const MapEntry &entry = entries[i];
if (entry.type & symmask)
{
char temp[256];
@ -449,6 +459,8 @@ void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask) const
void SymbolMap::FillListBoxBLinks(HWND listbox, int num) const
{
ListBox_ResetContent(listbox);
lock_guard guard(lock_);
int style = GetWindowLong(listbox,GWL_STYLE);

View file

@ -17,7 +17,8 @@
#pragma once
#include "../../Globals.h"
#include "Globals.h"
#include "native/base/mutex.h"
#include <vector>
#include <set>
#include <map>
@ -106,6 +107,7 @@ private:
std::set<MapEntryUniqueInfo> uniqueEntries;
std::vector<MapEntry> entries;
std::map<u32, u32> entryRanges;
mutable recursive_mutex lock_;
};
extern SymbolMap symbolMap;

View file

@ -401,8 +401,8 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
}
}
} else {
int tmp = GetIndex(kor_vowel, sw);
if(tmp == -1) {
int tmp3 = GetIndex(kor_vowel, sw);
if (tmp3 == -1) {
string += inputChars[i];
if (inputChars.size() < FieldMaxLength()) {
string += sw;
@ -431,29 +431,29 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1];
string += code;
code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp * 0x1C;
code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C;
string += code;
if(isInput == true) {
i_value[0] = kor_lconsSpr[tmp2 + 2];
i_value[1] = tmp;
i_value[1] = tmp3;
i_level = 2;
}
} else {
int tmp2 = GetIndex(kor_cons, kor_lcons[i_value[2]]);
int tmp4 = GetIndex(kor_cons, kor_lcons[i_value[2]]);
if(tmp2 != -1) {
if (tmp4 != -1) {
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
string += code;
code = 0xAC00 + tmp2 * 0x24C + tmp * 0x1C;
code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C;
string += code;
if(isInput == true) {
i_value[0] = tmp2;
i_value[1] = tmp;
i_value[0] = tmp4;
i_value[1] = tmp3;
i_level = 2;
}
} else {
@ -794,7 +794,7 @@ int PSPOskDialog::NativeKeyboard()
for (u32 i = 0, end = oskParams->fields[0].outtextlength; i < end; ++i)
{
u16 value = 0;
if (i < ARRAY_SIZE(input))
if (i < FieldMaxLength())
value = input[i];
outText[i] = value;
}

View file

@ -159,7 +159,7 @@ int PSPSaveDialog::Init(int paramAddr)
display = DS_NONE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // This run on PSP display a list of all save on the PSP. Weird. (Not really, it's to let you free up space)
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // When run on a PSP, displays a list of all saves on the PSP. Weird. (Not really, it's to let you free up space)
display = DS_DELETE_LIST_CHOICE;
break;
default:
@ -631,7 +631,7 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayMessage(d->T("Do you want to overwrite the data?"), true);
DisplayMessage(d->T("Confirm Overwrite","Do you want to overwrite the data?"), true);
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_SAVE);

View file

@ -15,6 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "base/logging.h"
#include "Core/Reporting.h"
#include "Core/Dialog/SavedataParam.h"
#include "Core/Dialog/PSPSaveDialog.h"
@ -157,6 +159,16 @@ void SavedataParam::Init()
{
pspFileSystem.MkDir(savePath);
}
// Create a nomedia file to hide save icons form Android image viewer
#ifdef ANDROID
int handle = pspFileSystem.OpenFile(savePath + ".nomedia", (FileAccess)(FILEACCESS_CREATE | FILEACCESS_WRITE), 0);
if (handle) {
ILOG("Created .nomedia file");
pspFileSystem.CloseFile(handle);
} else {
ILOG("Failed to create .nomedia file");
}
#endif
}
std::string SavedataParam::GetSaveDirName(SceUtilitySavedataParam* param, int saveId)

View file

@ -146,6 +146,14 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
}
break;
case R_MIPS_16:
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG_REPORT(LOADER, "WARNING: Unsupported R_MIPS_16 reloc @ %08x : %s", addr, temp);
}
break;
case 0: // another GP reloc!
{
char temp[256];
@ -158,7 +166,7 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG_REPORT(LOADER,"ARGH IT'S A UNKNOWN RELOCATION!!!!!!!! %08x, type=%d : %s", addr, type, temp);
ERROR_LOG_REPORT(LOADER,"ARGH IT'S AN UNKNOWN RELOCATION!!!!!!!! %08x, type=%d : %s", addr, type, temp);
}
break;
}

View file

@ -38,7 +38,7 @@ PBPReader::PBPReader(const char *filename) : header_() {
return;
}
INFO_LOG(LOADER, "Loading PBP, version = %08x", header_.version);
DEBUG_LOG(LOADER, "Loading PBP, version = %08x", header_.version);
}
u8 *PBPReader::GetSubFile(PBPSubFile file, size_t *outSize) {

View file

@ -243,7 +243,7 @@ bool DirectoryFileHandle::Open(std::string& basePath, std::string& fileName, Fil
size_t DirectoryFileHandle::Read(u8* pointer, s64 size)
{
size_t bytesRead;
size_t bytesRead = 0;
#ifdef _WIN32
::ReadFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0);
#else
@ -254,7 +254,7 @@ size_t DirectoryFileHandle::Read(u8* pointer, s64 size)
size_t DirectoryFileHandle::Write(const u8* pointer, s64 size)
{
size_t bytesWritten;
size_t bytesWritten = 0;
#ifdef _WIN32
::WriteFile(hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0);
#else

View file

@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <set>
#include <algorithm>
#include "Common/StringUtils.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/HLE/sceKernelThread.h"
@ -162,6 +163,7 @@ static bool RealPath(const std::string &currentDirectory, const std::string &inP
IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
{
lock_guard guard(lock);
for (size_t i = 0; i < fileSystems.size(); i++)
{
if (fileSystems[i].system->OwnsHandle(handle))
@ -171,9 +173,9 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
return 0;
}
extern u32 ioErrorCode;
bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, MountPoint **system)
{
lock_guard guard(lock);
std::string realpath;
// Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
@ -193,7 +195,7 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat
//Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
if (inpath.find(':') == std::string::npos /* means path is relative */)
{
ioErrorCode = SCE_KERNEL_ERROR_NOCWD;
lastOpenError = SCE_KERNEL_ERROR_NOCWD;
WARN_LOG_REPORT(HLE, "Path is relative, but current directory not set for thread %i. returning 8002032C(SCE_KERNEL_ERROR_NOCWD) instead.", currentThread);
}
}
@ -225,14 +227,25 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat
void MetaFileSystem::Mount(std::string prefix, IFileSystem *system)
{
lock_guard guard(lock);
MountPoint x;
x.prefix=prefix;
x.system=system;
x.prefix = prefix;
x.system = system;
fileSystems.push_back(x);
}
void MetaFileSystem::Unmount(std::string prefix, IFileSystem *system)
{
lock_guard guard(lock);
MountPoint x;
x.prefix = prefix;
x.system = system;
fileSystems.erase(std::remove(fileSystems.begin(), fileSystems.end(), x), fileSystems.end());
}
void MetaFileSystem::Shutdown()
{
lock_guard guard(lock);
current = 6;
// Ownership is a bit convoluted. Let's just delete everything once.
@ -252,8 +265,18 @@ void MetaFileSystem::Shutdown()
startingDirectory = "";
}
u32 MetaFileSystem::OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename)
{
lock_guard guard(lock);
u32 h = OpenFile(filename, access, devicename);
error = lastOpenError;
return h;
}
u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
{
lock_guard guard(lock);
lastOpenError = 0;
std::string of;
MountPoint *mount;
if (MapFilePath(filename, of, &mount))
@ -268,6 +291,7 @@ u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access, const char
PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
{
lock_guard guard(lock);
std::string of;
IFileSystem *system;
if (MapFilePath(filename, of, &system))
@ -283,6 +307,7 @@ PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath)
{
lock_guard guard(lock);
std::string of;
IFileSystem *system;
if (MapFilePath(inpath, of, &system)) {
@ -294,6 +319,7 @@ bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath
std::vector<PSPFileInfo> MetaFileSystem::GetDirListing(std::string path)
{
lock_guard guard(lock);
std::string of;
IFileSystem *system;
if (MapFilePath(path, of, &system))
@ -309,11 +335,13 @@ std::vector<PSPFileInfo> MetaFileSystem::GetDirListing(std::string path)
void MetaFileSystem::ThreadEnded(int threadID)
{
lock_guard guard(lock);
currentDir.erase(threadID);
}
int MetaFileSystem::ChDir(const std::string &dir)
{
lock_guard guard(lock);
// Retain the old path and fail if the arg is 1023 bytes or longer.
if (dir.size() >= 1023)
return SCE_KERNEL_ERROR_NAMETOOLONG;
@ -348,6 +376,7 @@ int MetaFileSystem::ChDir(const std::string &dir)
bool MetaFileSystem::MkDir(const std::string &dirname)
{
lock_guard guard(lock);
std::string of;
IFileSystem *system;
if (MapFilePath(dirname, of, &system))
@ -362,6 +391,7 @@ bool MetaFileSystem::MkDir(const std::string &dirname)
bool MetaFileSystem::RmDir(const std::string &dirname)
{
lock_guard guard(lock);
std::string of;
IFileSystem *system;
if (MapFilePath(dirname, of, &system))
@ -376,6 +406,7 @@ bool MetaFileSystem::RmDir(const std::string &dirname)
int MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
{
lock_guard guard(lock);
std::string of;
std::string rf;
IFileSystem *osystem;
@ -407,6 +438,7 @@ int MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
bool MetaFileSystem::RemoveFile(const std::string &filename)
{
lock_guard guard(lock);
std::string of;
IFileSystem *system;
if (MapFilePath(filename, of, &system))
@ -421,6 +453,7 @@ bool MetaFileSystem::RemoveFile(const std::string &filename)
void MetaFileSystem::CloseFile(u32 handle)
{
lock_guard guard(lock);
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
sys->CloseFile(handle);
@ -428,6 +461,7 @@ void MetaFileSystem::CloseFile(u32 handle)
size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
{
lock_guard guard(lock);
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
return sys->ReadFile(handle,pointer,size);
@ -437,6 +471,7 @@ size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
{
lock_guard guard(lock);
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
return sys->WriteFile(handle,pointer,size);
@ -446,6 +481,7 @@ size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
{
lock_guard guard(lock);
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
return sys->SeekFile(handle,position,type);
@ -455,6 +491,7 @@ size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
void MetaFileSystem::DoState(PointerWrap &p)
{
lock_guard guard(lock);
p.Do(current);
// Save/load per-thread current directory map

View file

@ -17,7 +17,8 @@
#pragma once
#include "FileSystem.h"
#include "native/base/mutex.h"
#include "Core/FileSystems/FileSystem.h"
class MetaFileSystem : public IHandleAllocator, public IFileSystem
{
@ -27,6 +28,11 @@ private:
{
std::string prefix;
IFileSystem *system;
bool operator == (const MountPoint &other) const
{
return prefix == other.prefix && system == other.system;
}
};
std::vector<MountPoint> fileSystems;
@ -34,6 +40,8 @@ private:
currentDir_t currentDir;
std::string startingDirectory;
int lastOpenError;
recursive_mutex lock;
public:
MetaFileSystem()
@ -42,7 +50,7 @@ public:
}
void Mount(std::string prefix, IFileSystem *system);
void Unmount(IFileSystem *system);
void Unmount(std::string prefix, IFileSystem *system);
void ThreadEnded(int threadID);
@ -72,7 +80,8 @@ public:
bool GetHostPath(const std::string &inpath, std::string &outpath);
std::vector<PSPFileInfo> GetDirListing(std::string path);
u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL);
u32 OpenFile(std::string filename, FileAccess access, const char *devicename = NULL);
u32 OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename = NULL);
void CloseFile(u32 handle);
size_t ReadFile(u32 handle, u8 *pointer, s64 size);
size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
@ -94,6 +103,7 @@ public:
// TODO: void IoCtl(...)
void SetStartingDirectory(const std::string &dir) {
lock_guard guard(lock);
startingDirectory = dir;
}
};

View file

@ -558,7 +558,6 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {
x.size = File::GetSize(fullName);
int fileIndex = getFileListIndex(filename);
x.startSector = fileList[fileIndex].firstBlock;
x.numSectors = (x.size+2047)/2048;

View file

@ -219,13 +219,13 @@ void PGF::ReadPtr(const u8 *ptr, size_t dataSize) {
}
// And shadow glyphs.
for (size_t i = 0; i < shadowGlyphs.size(); i++) {
for (size_t i = 0; i < glyphs.size(); i++) {
size_t shadowId = glyphs[i].shadowID;
if (shadowId < shadowMap.size()) {
if ((shadowId < shadowMap.size()) && (shadowId < shadowGlyphs.size())) {
size_t charId = shadowMap[shadowId];
if (charId < glyphs.size()) {
// TODO: check for pre existing shadow glyph
GetGlyph(fontData, charPointers[charId] * 4 * 8 /* ??? */, FONT_PGF_SHADOWGLYPH, shadowGlyphs[i]);
GetGlyph(fontData, charPointers[charId] * 4 * 8 /* ??? */, FONT_PGF_SHADOWGLYPH, shadowGlyphs[shadowId]);
}
}
}
@ -337,50 +337,86 @@ bool PGF::GetGlyph(const u8 *fontdata, size_t charPtr, int glyphType, Glyph &gly
glyph.shadowID = getBits(9, fontdata, charPtr);
charPtr += 9;
int dimensionIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
if ((glyph.flags & FONT_PGF_METRIC_DIMENSION_INDEX) == FONT_PGF_METRIC_DIMENSION_INDEX)
{
int dimensionIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
int xAdjustIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
if (dimensionIndex < header.dimTableLength) {
glyph.dimensionWidth = dimensionTable[0][dimensionIndex];
glyph.dimensionHeight = dimensionTable[1][dimensionIndex];
}
int yAdjustIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
if (dimensionIndex == 0 && isJPCSPFont(fileName.c_str())) {
// Fonts created by ttf2pgf do not contain complete Glyph information.
// Provide default values.
glyph.dimensionWidth = glyph.w << 6;
glyph.dimensionHeight = glyph.h << 6;
}
}
else
{
glyph.dimensionWidth = getBits(32, fontdata, charPtr);
charPtr += 32;
glyph.dimensionHeight = getBits(32, fontdata, charPtr);
charPtr += 32;
}
charPtr +=
((glyph.flags & FONT_PGF_METRIC_FLAG1) ? 0 : 56) +
((glyph.flags & FONT_PGF_METRIC_FLAG2) ? 0 : 56) +
((glyph.flags & FONT_PGF_METRIC_FLAG3) ? 0 : 56);
if ((glyph.flags & FONT_PGF_METRIC_BEARING_X_INDEX) == FONT_PGF_METRIC_BEARING_X_INDEX)
{
int xAdjustIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
if (xAdjustIndex < header.xAdjustTableLength) {
glyph.xAdjustH = xAdjustTable[0][xAdjustIndex];
glyph.xAdjustV = xAdjustTable[1][xAdjustIndex];
}
if (xAdjustIndex == 0 && isJPCSPFont(fileName.c_str()))
{
// Fonts created by ttf2pgf do not contain complete Glyph information.
// Provide default values.
glyph.xAdjustH = glyph.left << 6;
glyph.xAdjustV = glyph.left << 6;
}
}
else
{
glyph.xAdjustH = getBits(32, fontdata, charPtr);
charPtr += 32;
glyph.xAdjustV = getBits(32, fontdata, charPtr);
charPtr += 32;
}
if ((glyph.flags & FONT_PGF_METRIC_BEARING_Y_INDEX) == FONT_PGF_METRIC_BEARING_Y_INDEX)
{
int yAdjustIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
if (yAdjustIndex < header.xAdjustTableLength) {
glyph.yAdjustH = yAdjustTable[0][yAdjustIndex];
glyph.yAdjustV = yAdjustTable[1][yAdjustIndex];
}
if (yAdjustIndex == 0 && isJPCSPFont(fileName.c_str()))
{
// Fonts created by ttf2pgf do not contain complete Glyph information.
// Provide default values.
glyph.yAdjustH = glyph.top << 6;
glyph.yAdjustV = glyph.top << 6;
}
}
else
{
glyph.yAdjustH = getBits(32, fontdata, charPtr);
charPtr += 32;
glyph.yAdjustV = getBits(32, fontdata, charPtr);
charPtr += 32;
}
int advanceIndex = getBits(8, fontdata, charPtr);
charPtr += 8;
if (dimensionIndex < header.dimTableLength) {
glyph.dimensionWidth = dimensionTable[0][dimensionIndex];
glyph.dimensionHeight = dimensionTable[1][dimensionIndex];
}
if (xAdjustIndex < header.xAdjustTableLength) {
glyph.xAdjustH = xAdjustTable[0][xAdjustIndex];
glyph.xAdjustV = xAdjustTable[1][xAdjustIndex];
}
if (yAdjustIndex < header.xAdjustTableLength) {
glyph.yAdjustH = yAdjustTable[0][yAdjustIndex];
glyph.yAdjustV = yAdjustTable[1][yAdjustIndex];
}
if (dimensionIndex == 0 && xAdjustIndex == 0 && yAdjustIndex == 0 && isJPCSPFont(fileName.c_str())) {
// Fonts created by ttf2pgf do not contain complete Glyph information.
// Provide default values.
glyph.dimensionWidth = glyph.w << 6;
glyph.dimensionHeight = glyph.h << 6;
// This stuff doesn't exactly look right.
glyph.xAdjustH = glyph.left << 6;
glyph.xAdjustV = glyph.left << 6;
glyph.yAdjustH = glyph.top << 6;
glyph.yAdjustV = glyph.top << 6;
}
if (advanceIndex < header.advanceTableLength) {
glyph.advanceH = advanceTable[0][advanceIndex];
glyph.advanceV = advanceTable[1][advanceIndex];
@ -578,4 +614,4 @@ u32 GetFontPixelColor(int color, int pixelformat) {
}
return color;
}
}

View file

@ -35,9 +35,10 @@ enum {
FONT_PGF_BMP_H_ROWS = 0x01,
FONT_PGF_BMP_V_ROWS = 0x02,
FONT_PGF_BMP_OVERLAY = 0x03,
FONT_PGF_METRIC_FLAG1 = 0x04,
FONT_PGF_METRIC_FLAG2 = 0x08,
FONT_PGF_METRIC_FLAG3 = 0x10,
// Metric names according to JPCSP findings
FONT_PGF_METRIC_DIMENSION_INDEX = 0x04,
FONT_PGF_METRIC_BEARING_X_INDEX = 0x08,
FONT_PGF_METRIC_BEARING_Y_INDEX = 0x10,
FONT_PGF_CHARGLYPH = 0x20,
FONT_PGF_SHADOWGLYPH = 0x40,
};

View file

@ -1061,7 +1061,7 @@ int __AtracSetContext(Atrac *atrac)
// select the audio stream
ret = av_find_best_stream(atrac->pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, &pCodec, 0);
if (ret < 0) {
ERROR_LOG(HLE, "av_find_best_stream: Cannot find a audio stream in the input file %d", ret);
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
return -1;
}
atrac->audio_stream_index = ret;

View file

@ -431,6 +431,12 @@ int sceCccIsValidJIS(u32 c)
return c != 0;
}
int sceCccIsValidUnicode(u32 c)
{
WARN_LOG(HLE, "UNIMPL sceCccIsValidUnicode(%08x)", c);
return c != 0;
}
u32 sceCccSetErrorCharUTF8(u32 c)
{
DEBUG_LOG(HLE, "sceCccSetErrorCharUTF8(%08x)", c);
@ -507,6 +513,7 @@ const HLEFunction sceCcc[] =
{0x76e33e9c, WrapI_U<sceCccIsValidUCS2>, "sceCccIsValidUCS2"},
{0xd2b18485, WrapI_U<sceCccIsValidUCS4>, "sceCccIsValidUCS4"},
{0xa2d5d209, WrapI_U<sceCccIsValidJIS>, "sceCccIsValidJIS"},
{0xbd11eef3, WrapI_U<sceCccIsValidUnicode>, "sceCccIsValidUnicode"},
{0x17e1d813, WrapU_U<sceCccSetErrorCharUTF8>, "sceCccSetErrorCharUTF8"},
{0xb8476cf4, WrapU_U<sceCccSetErrorCharUTF16>, "sceCccSetErrorCharUTF16"},
{0xc56949ad, WrapU_U<sceCccSetErrorCharSJIS>, "sceCccSetErrorCharSJIS"},

View file

@ -77,7 +77,6 @@ static int holdMode;
static int mode;
static int width;
static int height;
static bool flipped;
// Don't include this in the state, time increases regardless of state.
static double curFrameTime;
static double nextFrameTime;
@ -412,30 +411,23 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
framebuf = latchedFramebuf;
framebufIsLatched = false;
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
gpuStats.numFlips++;
flipped = true;
}
}
gpuStats.numVBlanks++;
numVBlanksSinceFlip++;
// GTA hack - it doesn't flip, it stretch blits a secondary buffer on top of a static frame buffer, ugh.
if (numVBlanksSinceFlip == 2)
flipped = true;
if (g_Config.iShowFPSCounter) {
CalculateFPS();
}
if (flipped) {
flipped = false;
// We flip only if the framebuffer was dirty. This eliminates flicker when using
// non-buffered rendering. The interaction with frame skipping seems to need
// some work.
if (gpu->FramebufferDirty()) {
gpuStats.numFlips++;
// Draw screen overlays before blitting. Saves and restores the Ge context.
// Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity
// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
// anything to draw here.
bool wasSkipped = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0;
gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;
@ -452,7 +444,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
// Setting CORE_NEXTFRAME causes a swap.
// Check first though, might've just quit / been paused.
if (!wasSkipped) {
if (coreState == CORE_RUNNING) { // && gpu->FramebufferDirty()) {
if (coreState == CORE_RUNNING) {
coreState = CORE_NEXTFRAME;
gpu->CopyDisplayToOutput();
}
@ -524,8 +516,6 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync)
if (topaddr != framebuf.topaddr) {
framebuf = fbstate;
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
gpuStats.numFlips++;
flipped = true;
}
} else {
WARN_LOG(HLE, "%s: PSP_DISPLAY_SETBUF_IMMEDIATE without topaddr?", __FUNCTION__);

View file

@ -40,6 +40,11 @@ struct GeInterruptData
static std::list<GeInterruptData> ge_pending_cb;
static int geSyncEvent;
static int geInterruptEvent;
static int geCycleEvent;
// Let's try updating 10 times per vblank.
const int geIntervalUs = 1000000 / (60 * 10);
const int geBehindThresholdUs = 1000000 / (60 * 10);
class GeIntrHandler : public IntrHandler
{
@ -162,6 +167,20 @@ void __GeExecuteInterrupt(u64 userdata, int cyclesLate)
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_GE_INTR, PSP_INTR_SUB_NONE);
}
void __GeCheckCycles(u64 userdata, int cyclesLate)
{
u64 geTicks = gpu->GetTickEstimate();
if (geTicks != 0)
{
if (CoreTiming::GetTicks() > geTicks + usToCycles(geBehindThresholdUs)) {
u64 diff = CoreTiming::GetTicks() - geTicks;
gpu->SyncThread();
CoreTiming::Advance();
}
}
CoreTiming::ScheduleEvent(usToCycles(geIntervalUs), geCycleEvent, 0);
}
void __GeInit()
{
memset(&ge_used_callbacks, 0, sizeof(ge_used_callbacks));
@ -170,6 +189,12 @@ void __GeInit()
geSyncEvent = CoreTiming::RegisterEvent("GeSyncEvent", &__GeExecuteSync);
geInterruptEvent = CoreTiming::RegisterEvent("GeInterruptEvent", &__GeExecuteInterrupt);
geCycleEvent = CoreTiming::RegisterEvent("GeCycleEvent", &__GeCheckCycles);
// When we're using separate CPU/GPU threads, we need to keep them in sync.
if (IsOnSeparateCPUThread()) {
CoreTiming::ScheduleEvent(usToCycles(geIntervalUs), geCycleEvent, 0);
}
}
void __GeDoState(PointerWrap &p)
@ -182,6 +207,8 @@ void __GeDoState(PointerWrap &p)
CoreTiming::RestoreRegisterEvent(geSyncEvent, "GeSyncEvent", &__GeExecuteSync);
p.Do(geInterruptEvent);
CoreTiming::RestoreRegisterEvent(geInterruptEvent, "GeInterruptEvent", &__GeExecuteInterrupt);
p.Do(geCycleEvent);
CoreTiming::RestoreRegisterEvent(geCycleEvent, "GeCycleEvent", &__GeCheckCycles);
// Everything else is done in sceDisplay.
p.DoMarker("sceGe");
@ -215,14 +242,21 @@ bool __GeTriggerInterrupt(int listid, u32 pc, u64 atTicks)
return true;
}
void __GeWaitCurrentThread(WaitType type, SceUID waitId, const char *reason)
{
__KernelWaitCurThread(type, waitId, 0, 0, false, reason);
}
void __GeTriggerWait(WaitType type, SceUID waitId, const char *reason, bool noSwitch)
{
__KernelTriggerWait(type, waitId, 0, reason, noSwitch);
}
bool __GeHasPendingInterrupt()
{
return !ge_pending_cb.empty();
}
// The GE is implemented wrong - it should be parallel to the CPU execution instead of
// synchronous.
u32 sceGeEdramGetAddr()
{
u32 retVal = 0x04000000;
@ -378,7 +412,8 @@ int sceGeUnsetCallback(u32 cbID)
u32 sceGeSaveContext(u32 ctxAddr)
{
DEBUG_LOG(HLE, "sceGeSaveContext(%08x)", ctxAddr);
gpu->Flush();
gpu->SyncThread();
if (sizeof(gstate) > 512 * 4)
{
ERROR_LOG(HLE, "AARGH! sizeof(gstate) has grown too large!");
@ -399,7 +434,7 @@ u32 sceGeSaveContext(u32 ctxAddr)
u32 sceGeRestoreContext(u32 ctxAddr)
{
DEBUG_LOG(HLE, "sceGeRestoreContext(%08x)", ctxAddr);
gpu->Flush();
gpu->SyncThread();
if (sizeof(gstate) > 512 * 4)
{

View file

@ -43,6 +43,8 @@ void __GeDoState(PointerWrap &p);
void __GeShutdown();
bool __GeTriggerSync(WaitType waitType, int id, u64 atTicks);
bool __GeTriggerInterrupt(int listid, u32 pc, u64 atTicks);
void __GeWaitCurrentThread(WaitType type, SceUID waitId, const char *reason);
void __GeTriggerWait(WaitType type, SceUID waitId, const char *reason, bool noSwitch = false);
bool __GeHasPendingInterrupt();

View file

@ -16,33 +16,36 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstdlib>
#include "native/thread/thread.h"
#include "native/thread/threadutil.h"
#include "Core/Config.h"
#include "Core/System.h"
#include "Core/Host.h"
#include "Core/SaveState.h"
#include "HLE.h"
#include "Core/HLE/HLE.h"
#include "Core/MIPS/MIPS.h"
#include "Core/HW/MemoryStick.h"
#include "Core/HW/AsyncIOManager.h"
#include "Core/CoreTiming.h"
#include "Core/Reporting.h"
#include "../FileSystems/FileSystem.h"
#include "../FileSystems/MetaFileSystem.h"
#include "../FileSystems/ISOFileSystem.h"
#include "../FileSystems/DirectoryFileSystem.h"
#include "Core/FileSystems/FileSystem.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/FileSystems/ISOFileSystem.h"
#include "Core/FileSystems/DirectoryFileSystem.h"
extern "C" {
#include "ext/libkirk/amctrl.h"
};
#include "sceIo.h"
#include "sceRtc.h"
#include "sceKernel.h"
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#include "Core/HLE/sceIo.h"
#include "Core/HLE/sceRtc.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceKernelThread.h"
// For headless screenshots.
#include "sceDisplay.h"
#include "Core/HLE/sceDisplay.h"
const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002;
const int ERROR_ERRNO_FILE_ALREADY_EXISTS = 0x80010011;
@ -101,9 +104,14 @@ const int PSP_COUNT_FDS = 64;
// TODO: Should be 3, and stdin/stdout/stderr are special values aliased to 0?
const int PSP_MIN_FD = 4;
static int asyncNotifyEvent = -1;
static int syncNotifyEvent = -1;
static SceUID fds[PSP_COUNT_FDS];
static AsyncIOManager ioManager;
static bool ioManagerThreadEnabled = false;
static std::thread *ioManagerThread;
u32 ioErrorCode = 0;
// TODO: Is it better to just put all on the thread?
const int IO_THREAD_MIN_DATA_SIZE = 256;
#define SCE_STM_FDIR 0x1000
#define SCE_STM_FREG 0x2000
@ -294,40 +302,107 @@ void __IoAsyncNotify(u64 userdata, int cyclesLate) {
}
}
void __IoSyncNotify(u64 userdata, int cyclesLate) {
SceUID threadID = userdata >> 32;
int fd = (int) (userdata & 0xFFFFFFFF);
s64 result = -1;
u32 error;
FileNode *f = __IoGetFd(fd, error);
if (f) {
f->pendingAsyncResult = false;
f->hasAsyncResult = true;
AsyncIOResult managerResult;
if (ioManager.WaitResult(f->handle, managerResult)) {
result = managerResult;
} else {
ERROR_LOG(HLE, "Unable to complete IO operation.");
}
}
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_IO, error);
if (waitID == fd && error == 0) {
__KernelResumeThreadFromWait(threadID, result);
}
}
static DirectoryFileSystem *memstickSystem = NULL;
#ifdef ANDROID
static VFSFileSystem *flash0System = NULL;
#else
static DirectoryFileSystem *flash0System = NULL;
#endif
void __IoManagerThread() {
setCurrentThreadName("IOThread");
while (ioManagerThreadEnabled) {
ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000));
}
}
void __IoInit() {
INFO_LOG(HLE, "Starting up I/O...");
MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED);
asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify);
syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify);
std::string memstickpath;
std::string flash0path;
GetSysDirectories(memstickpath, flash0path);
DirectoryFileSystem *memstick = new DirectoryFileSystem(&pspFileSystem, memstickpath);
memstickSystem = new DirectoryFileSystem(&pspFileSystem, memstickpath);
#ifdef ANDROID
VFSFileSystem *flash0 = new VFSFileSystem(&pspFileSystem, "flash0");
flash0System = new VFSFileSystem(&pspFileSystem, "flash0");
#else
DirectoryFileSystem *flash0 = new DirectoryFileSystem(&pspFileSystem, flash0path);
flash0System = new DirectoryFileSystem(&pspFileSystem, flash0path);
#endif
pspFileSystem.Mount("ms0:", memstick);
pspFileSystem.Mount("fatms0:", memstick);
pspFileSystem.Mount("fatms:", memstick);
pspFileSystem.Mount("flash0:", flash0);
pspFileSystem.Mount("ms0:", memstickSystem);
pspFileSystem.Mount("fatms0:", memstickSystem);
pspFileSystem.Mount("fatms:", memstickSystem);
pspFileSystem.Mount("flash0:", flash0System);
__KernelListenThreadEnd(&TellFsThreadEnded);
memset(fds, 0, sizeof(fds));
ioManagerThreadEnabled = g_Config.bSeparateIOThread;
ioManager.SetThreadEnabled(ioManagerThreadEnabled);
if (ioManagerThreadEnabled) {
ioManagerThread = new std::thread(&__IoManagerThread);
}
}
void __IoDoState(PointerWrap &p) {
ioManager.DoState(p);
p.DoArray(fds, ARRAY_SIZE(fds));
p.Do(asyncNotifyEvent);
CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify);
p.Do(syncNotifyEvent);
CoreTiming::RestoreRegisterEvent(syncNotifyEvent, "IoSyncNotify", __IoSyncNotify);
p.DoMarker("sceIo");
}
void __IoShutdown() {
ioManagerThreadEnabled = false;
ioManager.SyncThread();
ioManager.FinishEventLoop();
if (ioManagerThread != NULL) {
delete ioManagerThread;
ioManagerThread = NULL;
}
pspFileSystem.Unmount("ms0:", memstickSystem);
pspFileSystem.Unmount("fatms0:", memstickSystem);
pspFileSystem.Unmount("fatms:", memstickSystem);
pspFileSystem.Unmount("flash0:", flash0System);
delete memstickSystem;
memstickSystem = NULL;
delete flash0System;
flash0System = NULL;
}
u32 __IoGetFileHandleFromId(u32 id, u32 &outError)
@ -386,6 +461,12 @@ void __IoCompleteAsyncIO(int fd) {
u32 error;
FileNode *f = __IoGetFd(fd, error);
if (f) {
AsyncIOResult managerResult;
if (ioManager.WaitResult(f->handle, managerResult)) {
f->asyncResult = managerResult;
} else {
// It's okay, not all operations are deferred.
}
if (f->callbackID) {
__KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg);
}
@ -432,6 +513,14 @@ void __IoSchedAsync(FileNode *f, int fd, int usec) {
f->hasAsyncResult = false;
}
void __IoSchedSync(FileNode *f, int fd, int usec) {
u64 param = ((u64)__KernelGetCurThread()) << 32 | fd;
CoreTiming::ScheduleEvent(usToCycles(usec), syncNotifyEvent, param);
f->pendingAsyncResult = true;
f->hasAsyncResult = false;
}
u32 sceIoGetstat(const char *filename, u32 addr) {
// TODO: Improve timing (although this seems normally slow..)
int usec = 1000;
@ -511,7 +600,7 @@ u32 npdrmRead(FileNode *f, u8 *data, int size) {
return size;
}
int __IoRead(int id, u32 data_addr, int size) {
bool __IoRead(int &result, int id, u32 data_addr, int size) {
if (id == 3) {
DEBUG_LOG(HLE, "sceIoRead STDIN");
return 0; //stdin
@ -520,58 +609,88 @@ int __IoRead(int id, u32 data_addr, int size) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if(!(f->openMode & FILEACCESS_READ))
{
return ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
}
else if (Memory::IsValidAddress(data_addr)) {
if (!(f->openMode & FILEACCESS_READ)) {
result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
return true;
} else if (Memory::IsValidAddress(data_addr)) {
u8 *data = (u8*) Memory::GetPointer(data_addr);
if(f->npdrm){
return npdrmRead(f, data, size);
}else{
return (int) pspFileSystem.ReadFile(f->handle, data, size);
if (f->npdrm) {
result = npdrmRead(f, data, size);
return true;
} else if (__KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE) {
AsyncIOEvent ev = IO_EVENT_READ;
ev.handle = f->handle;
ev.buf = data;
ev.bytes = size;
ioManager.ScheduleOperation(ev);
return false;
} else {
result = (int) pspFileSystem.ReadFile(f->handle, data, size);
return true;
}
} else {
ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr);
// TODO: Returning 0 because it wasn't being sign-extended in async result before.
// What should this do?
return 0;
result = 0;
return true;
}
} else {
ERROR_LOG(HLE, "sceIoRead ERROR: no file open");
return error;
result = error;
return true;
}
}
u32 sceIoRead(int id, u32 data_addr, int size) {
// TODO: Check id is valid first?
if (!__KernelIsDispatchEnabled() && id > 2)
if (!__KernelIsDispatchEnabled() && id > 2) {
return -1;
int result = __IoRead(id, data_addr, size);
if (result >= 0) {
DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
// TODO: Timing is probably not very accurate, low estimate.
int us = result/100;
if(us==0)
us = 100;
return hleDelayResult(result, "io read", us);
}
else
// TODO: Timing is probably not very accurate, low estimate.
int us = size / 100;
if (us < 100) {
us = 100;
}
int result;
bool complete = __IoRead(result, id, data_addr, size);
if (!complete) {
DEBUG_LOG(HLE, "sceIoRead(%d, %08x, %x): deferring result", id, data_addr, size);
u32 error;
FileNode *f = __IoGetFd(id, error);
__IoSchedSync(f, id, us);
__KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io read");
return 0;
} else if (result >= 0) {
DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
return hleDelayResult(result, "io read", us);
} else {
return result;
}
}
u32 sceIoReadAsync(int id, u32 data_addr, int size) {
// TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...)
int us = size / 100;
if (us < 100) {
us = 100;
}
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
f->asyncResult = __IoRead(id, data_addr, size);
// TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...)
int us = f->asyncResult/100;
if(us==0)
us = 100;
int result;
bool complete = __IoRead(result, id, data_addr, size);
if (complete) {
f->asyncResult = result;
DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
} else {
DEBUG_LOG(HLE, "sceIoReadAsync(%d, %08x, %x): deferring result", id, data_addr, size);
}
__IoSchedAsync(f, id, us);
DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
return 0;
} else {
ERROR_LOG(HLE, "sceIoReadAsync: bad file %d", id);
@ -579,24 +698,37 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) {
}
}
int __IoWrite(int id, void *data_ptr, int size) {
bool __IoWrite(int &result, int id, void *data_ptr, int size) {
// Let's handle stdout/stderr specially.
if (id == 1 || id == 2) {
const char *str = (const char *) data_ptr;
const int str_size = size == 0 ? 0 : (str[size - 1] == '\n' ? size - 1 : size);
INFO_LOG(HLE, "%s: %.*s", id == 1 ? "stdout" : "stderr", str_size, str);
return size;
result = size;
return true;
}
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if(!(f->openMode & FILEACCESS_WRITE)) {
return ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
if (!(f->openMode & FILEACCESS_WRITE)) {
result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR;
return true;
}
return (int) pspFileSystem.WriteFile(f->handle, (u8*) data_ptr, size);
if (__KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE) {
AsyncIOEvent ev = IO_EVENT_WRITE;
ev.handle = f->handle;
ev.buf = (u8 *) data_ptr;
ev.bytes = size;
ioManager.ScheduleOperation(ev);
return false;
} else {
result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size);
}
return true;
} else {
ERROR_LOG(HLE, "sceIoWrite ERROR: no file open");
return (s32) error;
result = (s32) error;
return true;
}
}
@ -605,33 +737,53 @@ u32 sceIoWrite(int id, u32 data_addr, int size) {
if (!__KernelIsDispatchEnabled() && id > 2)
return -1;
int result = __IoWrite(id, Memory::GetPointer(data_addr), size);
if (result >= 0) {
DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size);
// TODO: Timing is probably not very accurate, low estimate.
int us = result/100;
if(us==0)
us = 100;
if (__KernelIsDispatchEnabled())
return hleDelayResult(result, "io write", us);
else
return result;
// TODO: Timing is probably not very accurate, low estimate.
int us = size / 100;
if (us < 100) {
us = 100;
}
else
int result;
bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size);
if (!complete) {
DEBUG_LOG(HLE, "sceIoWrite(%d, %08x, %x): deferring result", id, data_addr, size);
u32 error;
FileNode *f = __IoGetFd(id, error);
__IoSchedSync(f, id, us);
__KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io write");
return 0;
} else if (result >= 0) {
DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size);
if (__KernelIsDispatchEnabled()) {
return hleDelayResult(result, "io write", us);
} else {
return result;
}
} else {
return result;
}
}
u32 sceIoWriteAsync(int id, u32 data_addr, int size) {
// TODO: Not sure what the correct delay is (and technically we shouldn't read from the buffer yet...)
int us = size / 100;
if (us < 100) {
us = 100;
}
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
f->asyncResult = __IoWrite(id, Memory::GetPointer(data_addr), size);
// TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...)
int us = f->asyncResult/100;
if(us==0)
us = 100;
int result;
bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size);
if (complete) {
f->asyncResult = result;
DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
} else {
DEBUG_LOG(HLE, "sceIoWriteAsync(%d, %08x, %x): deferring result", id, data_addr, size);
}
__IoSchedAsync(f, id, us);
DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
return 0;
} else {
ERROR_LOG(HLE, "sceIoWriteAsync: bad file %d", id);
@ -782,7 +934,7 @@ u32 sceIoLseek32Async(int id, int offset, int whence) {
return 0;
}
FileNode *__IoOpen(const char* filename, int flags, int mode) {
FileNode *__IoOpen(int &error, const char* filename, int flags, int mode) {
//memory stick filename
int access = FILEACCESS_NONE;
if (flags & O_RDONLY)
@ -796,9 +948,7 @@ FileNode *__IoOpen(const char* filename, int flags, int mode) {
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
ioErrorCode = 0;
u32 h = pspFileSystem.OpenFile(filename, (FileAccess) access);
u32 h = pspFileSystem.OpenWithError(error, filename, (FileAccess) access);
if (h == 0) {
return NULL;
}
@ -817,15 +967,16 @@ FileNode *__IoOpen(const char* filename, int flags, int mode) {
return f;
}
u32 sceIoOpen(const char* filename, int flags, int mode) {
u32 sceIoOpen(const char *filename, int flags, int mode) {
if (!__KernelIsDispatchEnabled())
return -1;
FileNode *f = __IoOpen(filename, flags, mode);
int error;
FileNode *f = __IoOpen(error, filename, flags, mode);
if (f == NULL)
{
//Timing is not accurate, aiming low for now.
if (ioErrorCode == SCE_KERNEL_ERROR_NOCWD)
// Timing is not accurate, aiming low for now.
if (error == (int)SCE_KERNEL_ERROR_NOCWD)
{
ERROR_LOG(HLE, "SCE_KERNEL_ERROR_NOCWD=sceIoOpen(%s, %08x, %08x) - no current working directory", filename, flags, mode);
return hleDelayResult(SCE_KERNEL_ERROR_NOCWD , "no cwd", 10000);
@ -1294,7 +1445,8 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode)
if (!__KernelIsDispatchEnabled())
sceKernelResumeDispatchThread(1);
FileNode *f = __IoOpen(filename, flags, mode);
int error;
FileNode *f = __IoOpen(error, filename, flags, mode);
int fd;
// We have to return an fd here, which may have been destroyed when we reach Wait if it failed.
@ -1305,7 +1457,7 @@ u32 sceIoOpenAsync(const char *filename, int flags, int mode)
f = new FileNode();
f->handle = kernelObjects.Create(f);
f->fullpath = filename;
f->asyncResult = ERROR_ERRNO_FILE_NOT_FOUND;
f->asyncResult = error == 0 ? ERROR_ERRNO_FILE_NOT_FOUND : error;
f->closePending = true;
fd = __IoAllocFd(f);

View file

@ -19,9 +19,9 @@
#include <string>
#include "../System.h"
#include "HLE.h"
#include "sceKernel.h"
#include "Core/System.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/sceKernel.h"
void __IoInit();
void __IoDoState(PointerWrap &p);

View file

@ -664,7 +664,6 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
if (!Memory::IsValidAddress(statusPtr))
return -1;
u32 error;
for (auto iter = e->waitingThreads.begin(); iter != e->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(iter->tid, WAITTYPE_EVENTFLAG, error);

View file

@ -1228,7 +1228,6 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr)
{
DEBUG_LOG(HLE, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr);
u32 error;
for (auto iter = vpl->waitingThreads.begin(); iter != vpl->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(iter->threadID, WAITTYPE_VPL, error);

View file

@ -704,7 +704,9 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
u8 vcount;
u16_le fcount;
u32_le resident;
u32_le extra;
u16_le vcountNew;
u8 unknown1;
u8 unknown2;
};
u32_le *entPos = (u32_le *)Memory::GetPointer(modinfo->libent);
@ -718,6 +720,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
continue;
}
u32_le variableCount = ent->size <= 4 ? ent->vcount : std::max((u32)ent->vcount , (u32)ent->vcountNew);
const char *name;
if (Memory::IsValidAddress(ent->name)) {
name = Memory::GetCharPointer(ent->name);
@ -737,10 +740,10 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
}
u32_le *residentPtr = (u32_le *)Memory::GetPointer(ent->resident);
u32_le *exportPtr = residentPtr + ent->fcount + ent->vcount;
u32_le *exportPtr = residentPtr + ent->fcount + variableCount;
if (ent->size != 4) {
WARN_LOG_REPORT(LOADER, "Unexpected export module entry size %d, extra=%08x", ent->size, ent->extra);
WARN_LOG_REPORT(LOADER, "Unexpected export module entry size %d, vcountNew=%08x, unknown1=%08x, unknown2=&08x", ent->size, ent->vcountNew, ent->unknown1, ent->unknown2);
}
for (u32 j = 0; j < ent->fcount; j++) {
@ -768,7 +771,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
}
}
for (u32 j = 0; j < ent->vcount; j++) {
for (u32 j = 0; j < variableCount; j++) {
u32 nid = residentPtr[ent->fcount + j];
u32 exportAddr = exportPtr[ent->fcount + j];

View file

@ -694,7 +694,6 @@ int sceKernelReferMutexStatus(SceUID id, u32 infoAddr)
// Don't write if the size is 0. Anything else is A-OK, though, apparently.
if (Memory::Read_U32(infoAddr) != 0)
{
u32 error;
for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_MUTEX, error);
@ -1153,7 +1152,6 @@ int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr)
{
auto workarea = m->nm.workarea;
u32 error;
for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_LWMUTEX, error);

View file

@ -333,7 +333,6 @@ int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
if (!Memory::IsValidAddress(infoPtr))
return -1;
u32 error;
for (auto iter = s->waitingThreads.begin(); iter != s->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_SEMA, error);

View file

@ -2690,7 +2690,7 @@ int sceKernelResumeThread(SceUID threadID)
DEBUG_LOG(HLE, "sceKernelResumeThread(%d)", threadID);
t->nt.status &= ~THREADSTATUS_SUSPEND;
// If it was dormant, waiting, etc. before we don't flip it's ready state.
// If it was dormant, waiting, etc. before we don't flip its ready state.
if (t->nt.status == 0)
__KernelChangeReadyState(t, threadID, true);
return 0;
@ -3539,6 +3539,8 @@ std::vector<DebugThreadInfo> GetThreadsInfo()
info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
info.status = t->nt.status;
info.entrypoint = t->nt.entrypoint;
info.initialStack = t->nt.initialStack;
info.stackSize = (u32)t->nt.stackSize;
info.priority = t->nt.currentPriority;
info.waitType = t->nt.waitType;
if(*iter == currentThread)

View file

@ -313,8 +313,10 @@ struct DebugThreadInfo
SceUID id;
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
u32 status;
int curPC;
int entrypoint;
u32 curPC;
u32 entrypoint;
u32 initialStack;
int stackSize;
int priority;
WaitType waitType;
bool isCurrent;

View file

@ -355,7 +355,7 @@ int sceMp3Init(u32 mp3) {
/* select the audio stream */
ret = av_find_best_stream(ctx->avformat_context, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);
if (ret < 0) {
ERROR_LOG(HLE, "av_find_best_stream: Cannot find a audio stream in the input file %d", ret);
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
return -1;
}
ctx->audio_stream_index = ret;

View file

@ -914,7 +914,7 @@ void PostPutAction::run(MipsCall &call) {
WARN_LOG(HLE, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer.packetsFree);
packetsAdded = ringbuffer.packetsFree;
}
int actuallyAdded = ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer.data), packetsAdded * 2048) / 2048;
int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer.data), packetsAdded * 2048) / 2048;
if (actuallyAdded != packetsAdded) {
WARN_LOG_REPORT(HLE, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames.");
}

View file

@ -28,7 +28,6 @@
#include <map>
// "Go Sudoku" is a good way to test this code...
const int size = 2048;
const int PSMF_VIDEO_STREAM_ID = 0xE0;
const int PSMF_AUDIO_STREAM_ID = 0xBD;
const int PSMF_AVC_STREAM = 0;

View file

@ -0,0 +1,97 @@
// 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/Reporting.h"
#include "Core/System.h"
#include "Core/HW/AsyncIOManager.h"
#include "Core/FileSystems/MetaFileSystem.h"
void AsyncIOManager::ScheduleOperation(AsyncIOEvent ev) {
lock_guard guard(resultsLock_);
resultsPending_.insert(ev.handle);
ScheduleEvent(ev);
}
bool AsyncIOManager::PopResult(u32 handle, AsyncIOResult &result) {
lock_guard guard(resultsLock_);
if (results_.find(handle) != results_.end()) {
result = results_[handle];
results_.erase(handle);
resultsPending_.erase(handle);
return true;
} else {
return false;
}
}
bool AsyncIOManager::WaitResult(u32 handle, AsyncIOResult &result) {
lock_guard guard(resultsLock_);
ScheduleEvent(IO_EVENT_SYNC);
while (HasEvents() && resultsPending_.find(handle) != resultsPending_.end()) {
if (PopResult(handle, result)) {
return true;
}
resultsWait_.wait_for(resultsLock_, 16);
}
if (PopResult(handle, result)) {
return true;
}
return false;
}
void AsyncIOManager::ProcessEvent(AsyncIOEvent ev) {
switch (ev.type) {
case IO_EVENT_READ:
Read(ev.handle, ev.buf, ev.bytes);
break;
case IO_EVENT_WRITE:
Write(ev.handle, ev.buf, ev.bytes);
break;
default:
ERROR_LOG(HLE, "Unsupported IO event type");
}
}
void AsyncIOManager::Read(u32 handle, u8 *buf, size_t bytes) {
size_t result = pspFileSystem.ReadFile(handle, buf, bytes);
EventResult(handle, result);
}
void AsyncIOManager::Write(u32 handle, u8 *buf, size_t bytes) {
size_t result = pspFileSystem.WriteFile(handle, buf, bytes);
EventResult(handle, result);
}
void AsyncIOManager::EventResult(u32 handle, AsyncIOResult result) {
lock_guard guard(resultsLock_);
if (results_.find(handle) != results_.end()) {
ERROR_LOG_REPORT(HLE, "Overwriting previous result for file action on handle %d", handle);
}
results_[handle] = result;
resultsWait_.notify_one();
}
void AsyncIOManager::DoState(PointerWrap &p) {
SyncThread();
lock_guard guard(resultsLock_);
p.Do(resultsPending_);
p.Do(results_);
p.DoMarker("AsyncIOManager");
}

75
Core/HW/AsyncIOManager.h Normal file
View file

@ -0,0 +1,75 @@
// 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 <map>
#include <set>
#include "native/base/mutex.h"
#include "Core/ThreadEventQueue.h"
class NoBase {
};
enum AsyncIOEventType {
IO_EVENT_INVALID,
IO_EVENT_SYNC,
IO_EVENT_FINISH,
IO_EVENT_READ,
IO_EVENT_WRITE,
};
struct AsyncIOEvent {
AsyncIOEvent(AsyncIOEventType t) : type(t) {}
AsyncIOEventType type;
u32 handle;
u8 *buf;
size_t bytes;
operator AsyncIOEventType() const {
return type;
}
};
// TODO: Something better.
typedef size_t AsyncIOResult;
typedef ThreadEventQueue<NoBase, AsyncIOEvent, AsyncIOEventType, IO_EVENT_INVALID, IO_EVENT_SYNC, IO_EVENT_FINISH> IOThreadEventQueue;
class AsyncIOManager : public IOThreadEventQueue {
public:
void DoState(PointerWrap &p);
void ScheduleOperation(AsyncIOEvent ev);
bool PopResult(u32 handle, AsyncIOResult &result);
bool WaitResult(u32 handle, AsyncIOResult &result);
protected:
virtual void ProcessEvent(AsyncIOEvent ref);
virtual bool ShouldExitEventLoop() {
return coreState == CORE_ERROR || coreState == CORE_POWERDOWN;
}
private:
void Read(u32 handle, u8 *buf, size_t bytes);
void Write(u32 handle, u8 *buf, size_t bytes);
void EventResult(u32 handle, AsyncIOResult result);
recursive_mutex resultsLock_;
condition_variable resultsWait_;
std::set<u32> resultsPending_;
std::map<u32, AsyncIOResult> results_;
};

View file

@ -46,7 +46,7 @@ bool isHeader(u8* audioStream, int offset)
return (audioStream[offset] == header1) && (audioStream[offset+1] == header2);
}
// header set to the headerbuf, and return it's size
// header set to the headerbuf, and return its size
int getOmaHeader(u8 codecId, u8 headerCode0, u8 headerCode1, u8 headerCode2, u8* headerbuf)
{
int pos = 0;

View file

@ -1,4 +1,4 @@
// It can simply convert a at3+ file or stream to oma format
// It can simply convert an at3+ file or stream to oma format
// Thanks to JPCSP project
#pragma once
@ -7,9 +7,9 @@
namespace OMAConvert {
// output OMA to outputStream, and return it's size. You need to release it by use releaseStream()
// output OMA to outputStream, and return its size. You need to release it by use releaseStream()
int convertStreamtoOMA(u8* audioStream, int audioSize, u8** outputStream);
// output OMA to outputStream, and return it's size. You need to release it by use releaseStream()
// output OMA to outputStream, and return its size. You need to release it by use releaseStream()
int convertRIFFtoOMA(u8* riff, int riffSize, u8** outputStream, int *readSize = 0);
void releaseStream(u8** stream);

View file

@ -27,7 +27,7 @@
#include "System.h"
// TODO : improve, look in the file more
EmuFileType Identify_File(std::string &filename)
IdentifiedFileType Identify_File(std::string &filename)
{
if (filename.size() == 0) {
ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
@ -73,7 +73,6 @@ EmuFileType Identify_File(std::string &filename)
return FILETYPE_ERROR;
}
u32_le id;
size_t readSize = fread(&id, 4, 1, f);
@ -83,31 +82,40 @@ EmuFileType Identify_File(std::string &filename)
}
u32 psar_offset = 0, psar_id = 0;
if (id == 'PBP\x00') {
switch (id) {
case 'PBP\x00':
fseek(f, 0x24, SEEK_SET);
fread(&psar_offset, 4, 1, f);
fseek(f, psar_offset, SEEK_SET);
fread(&psar_id, 4, 1, f);
break;
case '!raR':
return FILETYPE_ARCHIVE_RAR;
case '\x04\x03KP':
case '\x06\x05KP':
case '\x08\x07KP':
return FILETYPE_ARCHIVE_ZIP;
}
fclose(f);
if (id == 'FLE\x7F')
{
if (id == 'FLE\x7F') {
// There are a few elfs misnamed as pbp (like Trig Wars), accept that.
if (!strcasecmp(extension.c_str(), ".plf") || strstr(filename.c_str(),"BOOT.BIN") ||
!strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") ||
!strcasecmp(extension.c_str(), ".pbp"))
{
!strcasecmp(extension.c_str(), ".pbp")) {
return FILETYPE_PSP_ELF;
}
return FILETYPE_UNKNOWN_ELF;
}
else if (id == 'PBP\x00')
{
else if (id == 'PBP\x00') {
if (psar_id == 'MUPN') {
return FILETYPE_PSP_ISO_NP;
}
// PS1 PSAR begins with "PSISOIMG0000"
if (psar_id == 'SISP') {
return FILETYPE_PSP_PS1_PBP;
}
// Let's check if we got pointed to a PBP within such a directory.
// If so we just move up and return the directory itself as the game.
@ -120,14 +128,19 @@ EmuFileType Identify_File(std::string &filename)
}
return FILETYPE_PSP_PBP;
}
else if (!strcasecmp(extension.c_str(),".pbp"))
{
else if (!strcasecmp(extension.c_str(),".pbp")) {
ERROR_LOG(LOADER, "A PBP with the wrong magic number?");
return FILETYPE_PSP_PBP;
}
else if (!strcasecmp(extension.c_str(),".bin"))
{
} else if (!strcasecmp(extension.c_str(),".bin")) {
return FILETYPE_UNKNOWN_BIN;
} else if (!strcasecmp(extension.c_str(),".zip")) {
return FILETYPE_ARCHIVE_ZIP;
} else if (!strcasecmp(extension.c_str(),".rar")) {
return FILETYPE_ARCHIVE_RAR;
} else if (!strcasecmp(extension.c_str(),".r00")) {
return FILETYPE_ARCHIVE_RAR;
} else if (!strcasecmp(extension.c_str(),".r01")) {
return FILETYPE_ARCHIVE_RAR;
}
return FILETYPE_UNKNOWN;
}
@ -168,11 +181,23 @@ bool LoadFile(std::string &filename, std::string *error_string) {
pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR");
return Load_PSP_ISO(filename.c_str(), error_string);
case FILETYPE_PSP_PS1_PBP:
*error_string = "PS1 EBOOTs are not supported by PPSSPP.";
break;
case FILETYPE_ERROR:
ERROR_LOG(LOADER, "Could not read file");
*error_string = "Error reading file";
break;
case FILETYPE_ARCHIVE_RAR:
*error_string = "File is compressed (RAR).\nPlease decompress first (try WinRAR)";
break;
case FILETYPE_ARCHIVE_ZIP:
*error_string = "File is compressed (ZIP).\nPlease decompress first (try WinRAR)";
break;
case FILETYPE_UNKNOWN_BIN:
case FILETYPE_UNKNOWN_ELF:
case FILETYPE_UNKNOWN:

View file

@ -17,8 +17,7 @@
#pragma once
enum EmuFileType
{
enum IdentifiedFileType {
FILETYPE_ERROR,
FILETYPE_PSP_PBP_DIRECTORY,
@ -33,12 +32,17 @@ enum EmuFileType
FILETYPE_UNKNOWN_BIN,
FILETYPE_UNKNOWN_ELF,
// Try to reduce support emails...
FILETYPE_ARCHIVE_RAR,
FILETYPE_ARCHIVE_ZIP,
FILETYPE_PSP_PS1_PBP,
FILETYPE_UNKNOWN
};
// This can modify the string, for example for stripping off the "/EBOOT.PBP"
// for a FILETYPE_PSP_PBP_DIRECTORY.
EmuFileType Identify_File(std::string &str);
IdentifiedFileType Identify_File(std::string &str);
// Can modify the string filename, as it calls IdentifyFile above.
bool LoadFile(std::string &filename, std::string *error_string);

View file

@ -592,12 +592,22 @@ namespace MIPSComp
fpr.ReleaseSpillLocksAndDiscardTemps();
}
void Jit::Comp_Vhoriz(u32 op) {
DISABLE;
switch ((op >> 16) & 31) {
case 6: // vfad
break;
case 7: // vavg
break;
}
}
void Jit::Comp_VHdp(u32 op) {
// Similar to vdot
DISABLE;
}
void Jit::Comp_VecDo3(u32 op) {
CONDITIONAL_DISABLE;
@ -655,7 +665,7 @@ namespace MIPSComp
break;
// Unfortunately there is no VMIN/VMAX on ARM without NEON.
case 27: //VFPU3
switch ((op >> 23) & 3) {
switch ((op >> 23) & 7) {
case 2: // vmin
VCMP(fpr.V(sregs[i]), fpr.V(tregs[i]));
VMRS_APSR();
@ -674,6 +684,26 @@ namespace MIPSComp
VMOV(fpr.V(tempregs[i]), fpr.V(tregs[i]));
SetCC(CC_AL);
break;
case 6: // vsge
DISABLE; // pending testing
VCMP(fpr.V(tregs[i]), fpr.V(sregs[i]));
VMRS_APSR();
SetCC(CC_GE);
MOVI2F(fpr.V(tempregs[i]), 1.0f, R0);
SetCC(CC_LT);
MOVI2F(fpr.V(tempregs[i]), 0.0f, R0);
SetCC(CC_AL);
break;
case 7: // vslt
DISABLE; // pending testing
VCMP(fpr.V(tregs[i]), fpr.V(sregs[i]));
VMRS_APSR();
SetCC(CC_LT);
MOVI2F(fpr.V(tempregs[i]), 1.0f, R0);
SetCC(CC_GE);
MOVI2F(fpr.V(tempregs[i]), 0.0f, R0);
SetCC(CC_AL);
break;
}
break;
@ -1273,14 +1303,6 @@ namespace MIPSComp
fpr.ReleaseSpillLocksAndDiscardTemps();
}
void Jit::Comp_Vsge(u32 op) {
DISABLE;
}
void Jit::Comp_Vslt(u32 op) {
DISABLE;
}
void Jit::Comp_Vcmp(u32 op) {
CONDITIONAL_DISABLE;
@ -1523,10 +1545,6 @@ namespace MIPSComp
fpr.ReleaseSpillLocksAndDiscardTemps();
}
void Jit::Comp_Vhoriz(u32 op) {
DISABLE;
}
static float sincostemp[2];
void SinCos(float angle) {

View file

@ -25,6 +25,7 @@
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/MIPS/MIPSVFPUUtils.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "Core/HLE/sceDisplay.h"
@ -203,6 +204,7 @@ void MIPSState::WriteFCR(int reg, int value)
}
else
{
WARN_LOG_REPORT(CPU, "WriteFCR: Unexpected reg %d (value %08x)", reg, value);
// MessageBox(0, "Invalid FCR","...",0);
}
DEBUG_LOG(CPU, "FCR%i written to, value %08x", reg, value);
@ -222,6 +224,7 @@ u32 MIPSState::ReadFCR(int reg)
}
else
{
WARN_LOG_REPORT(CPU, "ReadFCR: Unexpected reg %d", reg);
// MessageBox(0, "Invalid FCR","...",0);
}
return 0;

View file

@ -54,7 +54,7 @@ namespace MIPSAnalyst
if (MIPS_GET_RT(opinfo) == reg)
return true;
}
if (opinfo & (IN_RS | IN_RS_ADDR | IN_RS_SHIFT))
if (opinfo & IN_RS)
{
if (MIPS_GET_RS(opinfo) == reg)
return true;
@ -87,75 +87,26 @@ namespace MIPSAnalyst
}
}
// Temporary, returns true for common ops which have proper flags in the table.
bool IsDelaySlotInfoSafe(u32 op)
{
const char *safeOps[] = {
"addi", "addiu", "slti", "sltiu", "andi", "ori", "xori", "lui",
"lb", "lh", "lwl", "lw", "lbu", "lhu", "lwr",
"sb", "sh", "swl", "sw", "swr",
"sll", "srl", "sra", "sllv", "srlv", "srav",
"add", "addu", "sub", "subu", "and", "or", "xor", "nor",
"slt", "sltu",
};
const char *opName = MIPSGetName(op);
for (size_t i = 0; i < ARRAY_SIZE(safeOps); ++i)
{
if (!strcmp(safeOps[i], opName))
return true;
}
return false;
}
bool IsDelaySlotNiceReg(u32 branchOp, u32 op, int reg1, int reg2)
{
// NOOPs are always nice.
if (op == 0)
return true;
// $0 is never an out reg, it's always 0.
if (reg1 != 0 && GetOutReg(op) == reg1)
return false;
if (reg2 != 0 && GetOutReg(op) == reg2)
return false;
// TODO: Once the flags are all correct on the tables, remove this safety.
if (IsDelaySlotInfoSafe(op))
{
// $0 is never an out reg, it's always 0.
if (reg1 != 0 && GetOutReg(op) == reg1)
return false;
if (reg2 != 0 && GetOutReg(op) == reg2)
return false;
return true;
}
return false;
return true;
}
bool IsDelaySlotNiceVFPU(u32 branchOp, u32 op)
{
// NOOPs are always nice.
if (op == 0)
return true;
// TODO: Once the flags are all correct on the tables, remove this safety.
if (IsDelaySlotInfoSafe(op))
{
// TODO: There may be IS_VFPU cases which are safe...
return (MIPSGetInfo(op) & IS_VFPU) == 0;
}
return false;
// TODO: There may be IS_VFPU cases which are safe...
return (MIPSGetInfo(op) & IS_VFPU) == 0;
}
bool IsDelaySlotNiceFPU(u32 branchOp, u32 op)
{
// NOOPs are always nice.
if (op == 0)
return true;
// TODO: Once the flags are all correct on the tables, remove this safety.
if (IsDelaySlotInfoSafe(op))
return (MIPSGetInfo(op) & OUT_FPUFLAG) == 0;
return false;
return (MIPSGetInfo(op) & OUT_FPUFLAG) == 0;
}
bool IsSyscall(u32 op)
@ -190,8 +141,7 @@ namespace MIPSAnalyst
int rd = MIPS_GET_RD(op);
if (
((info & IN_RS) && (rs == reg)) ||
((info & IN_RS_SHIFT) && (rs == reg)) ||
((info & IN_RS) && (info & IN_RS_ADDR) == IN_RS && (rs == reg)) ||
((info & IN_RT) && (rt == reg)))
{
if (regAnal[reg].firstRead == -1)
@ -293,13 +243,7 @@ namespace MIPSAnalyst
u32 op = Memory::Read_Instruction(addr);
u32 info = MIPSGetInfo(op);
if (
((info & IN_RS) ||
(info & IN_RS_SHIFT) ||
(info & IN_RS_ADDR))
&&
(MIPS_GET_RS(op) == reg)
)
if ((info & IN_RS) && (MIPS_GET_RS(op) == reg))
return true;
if ((info & IN_RT) && (MIPS_GET_RT(op) == reg))
return true;
@ -644,8 +588,12 @@ namespace MIPSAnalyst
info.dataSize = 2;
break;
case MEMTYPE_WORD:
case MEMTYPE_FLOAT:
info.dataSize = 4;
break;
case MEMTYPE_VQUAD:
info.dataSize = 16;
}
u32 rs = cpu->GetRegValue(0,MIPS_GET_RS(op));

View file

@ -26,14 +26,16 @@ namespace MIPSCodeUtils
#define FULLOP_JR_RA 0x03e00008
#define OP_SYSCALL 0x0000000c
#define OP_SYSCALL_MASK 0xFC00003F
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
u32 GetJumpTarget(u32 addr)
{
u32 op = Memory::Read_Instruction(addr);
if (op)
{
u32 maskedOp = op & 0xFC000000;
if (maskedOp == 0x0C000000 || maskedOp == 0x08000000) //jal
u32 info = MIPSGetInfo(op);
if ((info & IS_JUMP) && (info & IN_IMM26))
{
u32 target = (addr & 0xF0000000) | ((op&0x03FFFFFF) << 2);
return target;
@ -87,9 +89,40 @@ namespace MIPSCodeUtils
u32 info = MIPSGetInfo(op);
if (info & IS_CONDBRANCH)
{
//TODO: safer check
if ((op & 0xFFFF0000) == 0x10000000)
return addr + ((signed short)(op&0xFFFF)<<2);
bool sure;
bool takeBranch;
switch (info & CONDTYPE_MASK)
{
case CONDTYPE_EQ:
sure = _RS == _RT;
takeBranch = true;
break;
case CONDTYPE_NE:
sure = _RS == _RT;
takeBranch = false;
break;
case CONDTYPE_LEZ:
case CONDTYPE_GEZ:
sure = _RS == 0;
takeBranch = true;
break;
case CONDTYPE_LTZ:
case CONDTYPE_GTZ:
sure = _RS == 0;
takeBranch = false;
break;
default:
sure = false;
}
if (sure && takeBranch)
return addr + 4 + ((signed short)(op&0xFFFF)<<2);
else if (sure && !takeBranch)
return addr + 8;
else
return INVALIDTARGET;
}

View file

@ -230,6 +230,7 @@ namespace MIPSInt
int imm = (signed short)(op&0xFFFF)<<2;
u32 addr = PC + imm + 4;
// x, y, z, w, any, all, (invalid), (invalid)
int imm3 = (op>>18)&7;
int val = (currentMIPS->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
@ -290,7 +291,7 @@ namespace MIPSInt
_dbg_assert_msg_(CPU,0,"Jump in delay slot :(");
}
int rs = (op>>21)&0x1f;
int rs = _RS;
u32 addr = R(rs);
switch (op & 0x3f)
{
@ -339,10 +340,10 @@ namespace MIPSInt
void Int_StoreSync(u32 op)
{
s32 imm = (signed short)(op&0xFFFF);
int base = ((op >> 21) & 0x1f);
int rt = (op >> 16) & 0x1f;
u32 addr = R(base) + imm;
int imm = (signed short)(op&0xFFFF);
int rt = _RT;
int rs = _RS;
u32 addr = R(rs) + imm;
switch (op >> 26)
{
@ -946,7 +947,7 @@ namespace MIPSInt
{
case 0:
if (!reported) {
Reporting::ReportMessage("INTERRUPT instruction hit");
Reporting::ReportMessage("INTERRUPT instruction hit (%08x) at %08x", op, currentMIPS->pc);
WARN_LOG(CPU,"Disable/Enable Interrupt CPU instruction");
reported = 1;
}

View file

@ -215,31 +215,29 @@ namespace MIPSInt
switch (op >> 26)
{
case 53: //lvl.q/lvr.q
if (addr & 0x3)
{
_dbg_assert_msg_(CPU, 0, "Misaligned lvX.q");
}
if ((op&2) == 0)
{
// It's an LVL
float d[4];
ReadVector(d, V_Quad, vt);
int offset = (addr >> 2) & 3;
for (int i = 0; i < offset + 1; i++)
if (addr & 0x3)
{
d[3 - i] = Memory::Read_Float(addr - i * 4);
_dbg_assert_msg_(CPU, 0, "Misaligned lvX.q");
}
WriteVector(d, V_Quad, vt);
}
else
{
// It's an LVR
float d[4];
ReadVector(d, V_Quad, vt);
int offset = (addr >> 2) & 3;
for (int i = 0; i < (3 - offset) + 1; i++)
if ((op & 2) == 0)
{
d[i] = Memory::Read_Float(addr + 4 * i);
// It's an LVL
for (int i = 0; i < offset + 1; i++)
{
d[3 - i] = Memory::Read_Float(addr - 4 * i);
}
}
else
{
// It's an LVR
for (int i = 0; i < (3 - offset) + 1; i++)
{
d[i] = Memory::Read_Float(addr + 4 * i);
}
}
WriteVector(d, V_Quad, vt);
}
@ -254,33 +252,32 @@ namespace MIPSInt
break;
case 61: // svl.q/svr.q
if (addr & 0x3)
{
_dbg_assert_msg_(CPU, 0, "Misaligned svX.q");
}
if ((op&2) == 0)
{
// It's an SVL
if (addr & 0x3)
{
_dbg_assert_msg_(CPU, 0, "Misaligned svX.q");
}
float d[4];
ReadVector(d, V_Quad, vt);
int offset = (addr >> 2) & 3;
for (int i = 0; i < offset + 1; i++)
if ((op&2) == 0)
{
Memory::Write_Float(d[3 - i], addr - i * 4);
// It's an SVL
for (int i = 0; i < offset + 1; i++)
{
Memory::Write_Float(d[3 - i], addr - i * 4);
}
}
}
else
{
// It's an SVR
float d[4];
ReadVector(d, V_Quad, vt);
int offset = (addr >> 2) & 3;
for (int i = 0; i < (3 - offset) + 1; i++)
else
{
Memory::Write_Float(d[i], addr + 4 * i);
// It's an SVR
for (int i = 0; i < (3 - offset) + 1; i++)
{
Memory::Write_Float(d[i], addr + 4 * i);
}
}
break;
}
break;
case 62: //sv.q
if (addr & 0xF)
@ -331,6 +328,8 @@ namespace MIPSInt
case 7: m=one; break; // vmone
default:
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
PC += 4;
EatPrefixes();
return;
}
@ -353,6 +352,8 @@ namespace MIPSInt
case 7: v=ones; break; //vone
default:
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
PC += 4;
EatPrefixes();
return;
}
float o[4];
@ -1258,7 +1259,7 @@ namespace MIPSInt
case 1: d[i] = (float)currentMIPS->rng.R32(); break; // vrndi - TODO: copy bits instead?
case 2: d[i] = 1.0f + ((float)currentMIPS->rng.R32() / 0xFFFFFFFF); break; // vrndf1 TODO: make more accurate
case 3: d[i] = 2.0f + 2 * ((float)currentMIPS->rng.R32() / 0xFFFFFFFF); break; // vrndf2 TODO: make more accurate
case 4: d[i] = 0.0f; // Should not get here
default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
}
}
ApplyPrefixD(d, sz);
@ -1354,7 +1355,7 @@ namespace MIPSInt
{
s32 imm = (signed short)(op&0xFFFC);
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
int rs = (op >> 21) & 0x1f;
int rs = _RS;
u32 addr = R(rs) + imm;
switch (op >> 26)
@ -1532,6 +1533,8 @@ namespace MIPSInt
break;
default:
_dbg_assert_msg_(CPU,0,"unknown min/max op %d", cond);
PC += 4;
EatPrefixes();
return;
}
ApplyPrefixD(d, sz);
@ -1540,6 +1543,7 @@ namespace MIPSInt
EatPrefixes();
}
// This doesn't quite pass all the tests :/
void Int_Vscmp(u32 op) {
int vt = _VT;
int vs = _VS;

173
Core/MIPS/MIPSStackWalk.cpp Normal file
View file

@ -0,0 +1,173 @@
// 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/MemMap.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSStackWalk.h"
#define _RS ((op >> 21) & 0x1F)
#define _RT ((op >> 16) & 0x1F)
#define _RD ((op >> 11) & 0x1F)
#define _IMM16 ((signed short)(op & 0xFFFF))
#define MIPSTABLE_IMM_MASK 0xFC000000
#define MIPSTABLE_SPECIAL_MASK 0xFC00003F
namespace MIPSStackWalk {
using namespace MIPSCodeUtils;
// In the worst case, we scan this far above the pc for an entry.
const int MAX_FUNC_SIZE = 32768 * 4;
// After this we assume we're stuck.
const size_t MAX_DEPTH = 1024;
static u32 GuessEntry(u32 pc) {
SymbolInfo info;
if (symbolMap.GetSymbolInfo(&info, pc)) {
return info.address;
}
return INVALIDTARGET;
}
bool IsSWInstr(u32 op) {
return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;
}
bool IsAddImmInstr(u32 op) {
return (op & MIPSTABLE_IMM_MASK) == 0x20000000 || (op & MIPSTABLE_IMM_MASK) == 0x24000000;
}
bool IsMovRegsInstr(u32 op) {
// TODO: There are more options here. Let's assume addu for now.
if ((op & MIPSTABLE_SPECIAL_MASK) == 0x00000021) {
return _RS == 0 || _RT == 0;
}
return false;
}
bool ScanForAllocaSignature(u32 pc) {
// In God Eater Burst, for example, after 0880E750, there's what looks like an alloca().
// It's surrounded by "mov fp, sp" and "mov sp, fp", which is unlikely to be used for other reasons.
// It ought to be pretty close.
u32 stop = pc - 32 * 4;
for (; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
u32 op = Memory::Read_Instruction(pc);
// We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N".
if (IsMovRegsInstr(op) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) {
return true;
}
}
return false;
}
bool ScanForEntry(StackFrame &frame, u32 entry, u32 &ra) {
// TODO: Check if found entry is in the same symbol? Might be wrong sometimes...
int ra_offset = -1;
u32 stop = entry == INVALIDTARGET ? 0 : entry;
for (u32 pc = frame.pc; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
u32 op = Memory::Read_Instruction(pc);
// Here's where they store the ra address.
if (IsSWInstr(op) && _RT == MIPS_REG_RA && _RS == MIPS_REG_SP) {
ra_offset = _IMM16;
}
if (IsAddImmInstr(op) && _RT == MIPS_REG_SP && _RS == MIPS_REG_SP) {
// A positive imm either means alloca() or we went too far.
if (_IMM16 > 0) {
// TODO: Maybe check for any alloca() signature and bail?
continue;
}
if (ScanForAllocaSignature(pc)) {
continue;
}
frame.entry = pc;
frame.stackSize = -_IMM16;
if (ra_offset != -1) {
ra = Memory::Read_U32(frame.sp + ra_offset);
}
return true;
}
}
return false;
}
bool DetermineFrameInfo(StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) {
if (ScanForEntry(frame, possibleEntry, ra)) {
// Awesome, found one that looks right.
return true;
} else if (ra != INVALIDTARGET && possibleEntry != INVALIDTARGET) {
// Let's just assume it's a leaf.
frame.entry = possibleEntry;
frame.stackSize = 0;
return true;
}
// Okay, we failed to get one. Our possibleEntry could be wrong, it often is.
// Let's just scan upward.
u32 newPossibleEntry = frame.pc > threadEntry ? threadEntry : frame.pc - MAX_FUNC_SIZE;
if (ScanForEntry(frame, newPossibleEntry, ra)) {
return true;
} else {
return false;
}
}
std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) {
std::vector<StackFrame> frames;
StackFrame current;
current.pc = pc;
current.sp = sp;
current.entry = INVALIDTARGET;
current.stackSize = -1;
u32 prevEntry = INVALIDTARGET;
while (pc != threadEntry) {
u32 possibleEntry = GuessEntry(current.pc);
if (DetermineFrameInfo(current, possibleEntry, threadEntry, ra)) {
frames.push_back(current);
if (current.entry == threadEntry || GuessEntry(current.entry) == threadEntry) {
break;
}
if (current.entry == prevEntry || frames.size() >= MAX_DEPTH) {
// Recursion, means we're screwed. Let's just give up.
break;
}
prevEntry = current.entry;
current.pc = ra;
current.sp += current.stackSize;
ra = INVALIDTARGET;
current.entry = INVALIDTARGET;
current.stackSize = -1;
} else {
// Well, we got as far as we could.
current.entry = possibleEntry;
current.stackSize = 0;
frames.push_back(current);
break;
}
}
return frames;
}
};

36
Core/MIPS/MIPSStackWalk.h Normal file
View file

@ -0,0 +1,36 @@
// 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
#include <vector>
#include "Common/CommonTypes.h"
namespace MIPSStackWalk {
struct StackFrame {
// Beginning of function symbol (may be estimated.)
u32 entry;
// Next position within function.
u32 pc;
// Value of SP inside this function (assuming no alloca()...)
u32 sp;
// Size of stack frame in bytes.
int stackSize;
};
std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop);
};

File diff suppressed because it is too large Load diff

View file

@ -30,10 +30,12 @@
// as long as the other flags are checked,
// there is no way to misinterprete these
// as CONDTYPE_X
#define MEMTYPE_MASK 0x00000003
#define MEMTYPE_MASK 0x00000007
#define MEMTYPE_BYTE 0x00000001
#define MEMTYPE_HWORD 0x00000002
#define MEMTYPE_WORD 0x00000003
#define MEMTYPE_FLOAT 0x00000004
#define MEMTYPE_VQUAD 0x00000005
#define IS_CONDMOVE 0x00000008
#define DELAYSLOT 0x00000010
@ -43,9 +45,9 @@
#define IS_CONDBRANCH 0x00000100
#define IS_JUMP 0x00000200
#define IN_RS_SHIFT 0x00000400
#define IN_RS_ADDR 0x00000800
#define IN_RS 0x00001000
#define IN_RS 0x00000400
#define IN_RS_ADDR (0x00000800 | IN_RS)
#define IN_RS_SHIFT (0x00001000 | IN_RS)
#define IN_RT 0x00002000
#define IN_SA 0x00004000
#define IN_IMM16 0x00008000

View file

@ -87,7 +87,7 @@ void Jit::Comp_FPU3op(u32 op)
}
}
static u32 GC_ALIGNED16(ssLoadStoreTemp);
static u32 MEMORY_ALIGNED16(ssLoadStoreTemp);
void Jit::Comp_FPULS(u32 op)
{
@ -147,9 +147,9 @@ void Jit::Comp_FPULS(u32 op)
}
}
static const u64 GC_ALIGNED16(ssOneBits[2]) = {0x0000000100000001ULL, 0x0000000100000001ULL};
static const u64 GC_ALIGNED16(ssSignBits2[2]) = {0x8000000080000000ULL, 0x8000000080000000ULL};
static const u64 GC_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL};
static const u64 MEMORY_ALIGNED16(ssOneBits[2]) = {0x0000000100000001ULL, 0x0000000100000001ULL};
static const u64 MEMORY_ALIGNED16(ssSignBits2[2]) = {0x8000000080000000ULL, 0x8000000080000000ULL};
static const u64 MEMORY_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL};
static u32 ssCompareTemp;

View file

@ -57,8 +57,9 @@ static const float one = 1.0f;
static const float minus_one = -1.0f;
static const float zero = 0.0f;
const u32 GC_ALIGNED16( noSignMask[4] ) = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF};
const u32 GC_ALIGNED16( signBitLower[4] ) = {0x80000000, 0, 0, 0};
const u32 MEMORY_ALIGNED16( noSignMask[4] ) = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF};
const u32 MEMORY_ALIGNED16( signBitLower[4] ) = {0x80000000, 0, 0, 0};
const float MEMORY_ALIGNED16( oneOneOneOne[4] ) = {1.0f, 1.0f, 1.0f, 1.0f};
void Jit::Comp_VPFX(u32 op)
{
@ -195,7 +196,7 @@ bool IsOverlapSafe(int dreg, int di, int sn, u8 sregs[], int tn = 0, u8 tregs[]
return IsOverlapSafeAllowS(dreg, di, sn, sregs, tn, tregs) && sregs[di] != dreg;
}
static u32 GC_ALIGNED16(ssLoadStoreTemp);
static u32 MEMORY_ALIGNED16(ssLoadStoreTemp);
void Jit::Comp_SV(u32 op) {
CONDITIONAL_DISABLE;
@ -271,6 +272,73 @@ void Jit::Comp_SVQ(u32 op)
switch (op >> 26)
{
case 53: //lvl.q/lvr.q
{
if (!g_Config.bFastMemory) {
DISABLE;
}
DISABLE;
gpr.BindToRegister(rs, true, true);
gpr.FlushLockX(ECX);
u8 vregs[4];
GetVectorRegs(vregs, V_Quad, vt);
MOV(32, R(EAX), gpr.R(rs));
ADD(32, R(EAX), Imm32(imm));
#ifdef _M_IX86
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
#endif
MOV(32, R(ECX), R(EAX));
SHR(32, R(EAX), Imm8(2));
AND(32, R(EAX), Imm32(0x3));
CMP(32, R(EAX), Imm32(0));
FixupBranch next = J_CC(CC_NE);
fpr.MapRegsV(vregs, V_Quad, MAP_DIRTY);
// Offset = 0
MOVSS(fpr.RX(vregs[3]), MRegSum(RBX, RAX));
FixupBranch skip0 = J();
SetJumpTarget(next);
CMP(32, R(EAX), Imm32(1));
next = J_CC(CC_NE);
// Offset = 1
MOVSS(fpr.RX(vregs[3]), MComplex(RBX, RAX, 1, 4));
MOVSS(fpr.RX(vregs[2]), MComplex(RBX, RAX, 1, 0));
FixupBranch skip1 = J();
SetJumpTarget(next);
CMP(32, R(EAX), Imm32(2));
next = J_CC(CC_NE);
// Offset = 2
MOVSS(fpr.RX(vregs[3]), MComplex(RBX, RAX, 1, 8));
MOVSS(fpr.RX(vregs[2]), MComplex(RBX, RAX, 1, 4));
MOVSS(fpr.RX(vregs[1]), MComplex(RBX, RAX, 1, 0));
FixupBranch skip2 = J();
SetJumpTarget(next);
CMP(32, R(EAX), Imm32(3));
next = J_CC(CC_NE);
// Offset = 3
MOVSS(fpr.RX(vregs[3]), MComplex(RBX, RAX, 1, 12));
MOVSS(fpr.RX(vregs[2]), MComplex(RBX, RAX, 1, 8));
MOVSS(fpr.RX(vregs[1]), MComplex(RBX, RAX, 1, 4));
MOVSS(fpr.RX(vregs[0]), MComplex(RBX, RAX, 1, 0));
SetJumpTarget(next);
SetJumpTarget(skip0);
SetJumpTarget(skip1);
SetJumpTarget(skip2);
gpr.UnlockAll();
fpr.ReleaseSpillLocks();
}
break;
case 54: //lv.q
{
gpr.BindToRegister(rs, true, true);
@ -336,6 +404,8 @@ void Jit::Comp_SVQ(u32 op)
}
break;
default:
DISABLE;
break;
@ -658,50 +728,43 @@ void Jit::Comp_VecDo3(u32 op) {
if (js.HasUnknownPrefix())
DISABLE;
void (XEmitter::*xmmop)(X64Reg, OpArg) = NULL;
switch (op >> 26)
{
// Check that we can support the ops, and prepare temporary values for ops that need it.
switch (op >> 26) {
case 24: //VFPU0
switch ((op >> 23)&7)
{
switch ((op >> 23) & 7) {
case 0: // d[i] = s[i] + t[i]; break; //vadd
xmmop = &XEmitter::ADDSS;
break;
case 1: // d[i] = s[i] - t[i]; break; //vsub
xmmop = &XEmitter::SUBSS;
break;
case 7: // d[i] = s[i] / t[i]; break; //vdiv
xmmop = &XEmitter::DIVSS;
break;
default:
DISABLE;
}
break;
case 25: //VFPU1
switch ((op >> 23) & 7)
{
switch ((op >> 23) & 7) {
case 0: // d[i] = s[i] * t[i]; break; //vmul
xmmop = &XEmitter::MULSS;
break;
default:
DISABLE;
}
break;
case 27: //VFPU3
switch ((op >> 23) & 3)
{
switch ((op >> 23) & 7) {
case 2: // vmin
xmmop = &XEmitter::MINSS;
break;
case 3: // vmax
xmmop = &XEmitter::MAXSS;
break;
case 6: // vsge
case 7: // vslt
break;
default:
DISABLE;
}
break;
default:
_dbg_assert_msg_(CPU,0,"invalid VecDo3");
DISABLE;
break;
}
if (xmmop == NULL)
DISABLE;
VectorSize sz = GetVecSize(op);
int n = GetNumVectorElements(sz);
@ -740,8 +803,50 @@ void Jit::Comp_VecDo3(u32 op) {
MOVSS(tempxregs[i], fpr.V(sregs[i]));
}
for (int i = 0; i < n; ++i)
(this->*xmmop)(tempxregs[i], fpr.V(tregs[i]));
for (int i = 0; i < n; ++i) {
switch (op >> 26) {
case 24: //VFPU0
switch ((op >> 23) & 7) {
case 0: // d[i] = s[i] + t[i]; break; //vadd
ADDSS(tempxregs[i], fpr.V(tregs[i]));
break;
case 1: // d[i] = s[i] - t[i]; break; //vsub
SUBSS(tempxregs[i], fpr.V(tregs[i]));
break;
case 7: // d[i] = s[i] / t[i]; break; //vdiv
DIVSS(tempxregs[i], fpr.V(tregs[i]));
break;
}
break;
case 25: //VFPU1
switch ((op >> 23) & 7)
{
case 0: // d[i] = s[i] * t[i]; break; //vmul
MULSS(tempxregs[i], fpr.V(tregs[i]));
break;
}
break;
case 27: //VFPU3
switch ((op >> 23) & 7)
{
case 2: // vmin
MINSS(tempxregs[i], fpr.V(tregs[i]));
break;
case 3: // vmax
MAXSS(tempxregs[i], fpr.V(tregs[i]));
break;
case 6: // vsge
CMPNLTSS(tempxregs[i], fpr.V(tregs[i]));
ANDPS(tempxregs[i], M((void *)&oneOneOneOne));
break;
case 7: // vslt
CMPLTSS(tempxregs[i], fpr.V(tregs[i]));
ANDPS(tempxregs[i], M((void *)&oneOneOneOne));
break;
}
break;
}
}
for (int i = 0; i < n; ++i)
{

View file

@ -22,6 +22,7 @@
#include "Core/System.h"
#include "Core/CoreTiming.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
@ -278,11 +279,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
fpr.Start(mips_, analysis);
js.numInstructions = 0;
while (js.compiling)
{
if (js.prefixS & 0xF0000000) {
ERROR_LOG(CPU, "GARBAGE prefix S : %08x at %08x : %s", js.prefixS, js.compilerPC, currentMIPS->DisasmAt(js.compilerPC));
}
while (js.compiling) {
// Jit breakpoints are quite fast, so let's do them in release too.
CheckJitBreakpoint(js.compilerPC, 0);
@ -291,8 +288,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
MIPSCompileOp(inst);
if (js.afterOp & JitState::AFTER_CORE_STATE)
{
if (js.afterOp & JitState::AFTER_CORE_STATE) {
// TODO: Save/restore?
FlushAll();
@ -314,9 +310,6 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
js.compilerPC += 4;
js.numInstructions++;
}
if (js.prefixS & 0xF0000000) {
ERROR_LOG(CPU, "GARBAGE prefix S : %08x at %08x : %s", js.prefixS, js.compilerPC, currentMIPS->DisasmAt(js.compilerPC));
}
b->codeSize = (u32)(GetCodePtr() - b->normalEntry);
NOP();
@ -346,7 +339,7 @@ void Jit::Comp_Generic(u32 op)
ABI_CallFunctionC((void *)func, op);
}
else
_dbg_assert_msg_(JIT, 0, "Trying to compile instruction that can't be interpreted");
ERROR_LOG_REPORT(JIT, "Trying to compile instruction that can't be interpreted");
const int info = MIPSGetInfo(op);
if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0)
@ -360,7 +353,7 @@ void Jit::Comp_Generic(u32 op)
void Jit::WriteExit(u32 destination, int exit_num)
{
if (!Memory::IsValidAddress(destination)) {
ERROR_LOG(JIT, "Trying to write block exit to illegal destination %08x: pc = %08x", destination, currentMIPS->pc);
ERROR_LOG_REPORT(JIT, "Trying to write block exit to illegal destination %08x: pc = %08x", destination, currentMIPS->pc);
}
// If we need to verify coreState and rewind, we may not jump yet.
if (js.afterOp & (JitState::AFTER_CORE_STATE | JitState::AFTER_REWIND_PC_BAD_STATE))

View file

@ -165,16 +165,6 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength)
}
}
void Memcpy(const u32 to_address, const void *from_data, const u32 len)
{
memcpy(GetPointer(to_address), from_data, len);
}
void Memcpy(void *to_data, const u32 from_address, const u32 len)
{
memcpy(to_data,GetPointer(from_address),len);
}
void GetString(std::string& _string, const u32 em_address)
{
char stringBuffer[2048];

View file

@ -117,6 +117,13 @@ u64 Read_U64(const u32 _Address);
#define _M_ARM
#endif
inline u8* GetPointerUnchecked(const u32 address) {
#if defined(_M_IX86) || defined(_M_ARM32)
return (u8 *)(base + (address & MEMVIEW32_MASK));
#else
return (u8 *)(base + address);
#endif
}
#ifdef SAFE_MEMORY
u32 ReadUnchecked_U32(const u32 _Address);
@ -212,8 +219,30 @@ inline const char* GetCharPointer(const u32 address) {
}
void Memset(const u32 _Address, const u8 _Data, const u32 _iLength);
void Memcpy(const u32 to_address, const void *from_data, const u32 len);
void Memcpy(void *to_data, const u32 from_address, const u32 len);
inline void Memcpy(const u32 to_address, const void *from_data, const u32 len)
{
u8 *to = GetPointer(to_address);
if (to) {
memcpy(to, from_data, len);
}
// if not, GetPointer will log.
}
inline void Memcpy(void *to_data, const u32 from_address, const u32 len)
{
const u8 *from = GetPointer(from_address);
if (from) {
memcpy(to_data, from, len);
}
// if not, GetPointer will log.
}
inline void MemcpyUnchecked(void *to_data, const u32 from_address, const u32 len)
{
memcpy(to_data, GetPointerUnchecked(from_address), len);
}
template<class T>
void ReadStruct(u32 address, T *ptr)

View file

@ -88,6 +88,7 @@ namespace SaveState
if (Core_IsInactive() && __KernelIsRunning())
{
// Warning: this may run on a different thread.
needsProcess = true;
Process();
}
else

View file

@ -19,6 +19,10 @@
#include "Common/CommonWindows.h"
#endif
#include "native/thread/thread.h"
#include "native/thread/threadutil.h"
#include "native/base/mutex.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
@ -41,29 +45,80 @@
#include "Core/SaveState.h"
#include "Common/LogManager.h"
#include "GPU/GPUState.h"
#include "GPU/GPUInterface.h"
enum CPUThreadState {
CPU_THREAD_NOT_RUNNING,
CPU_THREAD_PENDING,
CPU_THREAD_STARTING,
CPU_THREAD_RUNNING,
CPU_THREAD_SHUTDOWN,
CPU_THREAD_EXECUTE,
};
MetaFileSystem pspFileSystem;
ParamSFOData g_paramSFO;
GlobalUIState globalUIState;
static CoreParameter coreParameter;
static PSPMixer *mixer;
static std::thread *cpuThread = NULL;
static recursive_mutex cpuThreadLock;
static condition_variable cpuThreadCond;
static u64 cpuThreadUntil;
// This can be read and written from ANYWHERE.
volatile CoreState coreState = CORE_STEPPING;
// Note: intentionally not used for CORE_NEXTFRAME.
volatile bool coreStatePending = false;
static volatile CPUThreadState cpuThreadState = CPU_THREAD_NOT_RUNNING;
void Core_UpdateState(CoreState newState)
{
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
coreStatePending = true;
coreState = newState;
bool IsOnSeparateCPUThread() {
if (cpuThread != NULL) {
return cpuThread->get_id() == std::this_thread::get_id();
} else {
return false;
}
}
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
{
INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION);
bool CPU_NextState(CPUThreadState from, CPUThreadState to) {
if (cpuThreadState == from) {
cpuThreadState = to;
cpuThreadCond.notify_one();
return true;
} else {
return false;
}
}
coreParameter = coreParam;
void CPU_SetState(CPUThreadState to) {
cpuThreadState = to;
cpuThreadCond.notify_one();
}
bool CPU_IsReady() {
return cpuThreadState == CPU_THREAD_RUNNING || cpuThreadState == CPU_THREAD_NOT_RUNNING;
}
bool CPU_IsShutdown() {
return cpuThreadState == CPU_THREAD_NOT_RUNNING;
}
bool CPU_HasPendingAction() {
return cpuThreadState != CPU_THREAD_RUNNING;
}
void CPU_WaitStatus(bool (*pred)()) {
cpuThreadLock.lock();
while (!pred())
cpuThreadCond.wait_for(cpuThreadLock, 16);
cpuThreadLock.unlock();
}
void CPU_Shutdown();
void CPU_Init() {
currentCPU = &mipsr4k;
numCPUs = 1;
@ -73,11 +128,18 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
g_RemasterMode = false;
g_DoubleTextureCoordinates = false;
std::string filename = coreParam.fileToStart;
EmuFileType type = Identify_File(filename);
std::string filename = coreParameter.fileToStart;
IdentifiedFileType type = Identify_File(filename);
if(type == FILETYPE_PSP_ISO || type == FILETYPE_PSP_ISO_NP || type == FILETYPE_PSP_DISC_DIRECTORY)
switch (type) {
case FILETYPE_PSP_ISO:
case FILETYPE_PSP_ISO_NP:
case FILETYPE_PSP_DISC_DIRECTORY:
InitMemoryForGameISO(filename);
break;
default:
break;
}
Memory::Init();
mipsr4k.Reset();
@ -85,14 +147,12 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
host->AttemptLoadSymbolMap();
if (coreParameter.enableSound)
{
if (coreParameter.enableSound) {
mixer = new PSPMixer();
host->InitSound(mixer);
}
if (coreParameter.disableG3Dlog)
{
if (coreParameter.disableG3Dlog) {
LogManager::GetInstance()->SetEnable(LogTypes::G3D, false);
}
@ -104,64 +164,148 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string)
// TODO: Check Game INI here for settings, patches and cheats, and modify coreParameter accordingly
// Why did we check for CORE_POWERDOWN here?
if (!LoadFile(filename, error_string)) { // || coreState == CORE_POWERDOWN) {
pspFileSystem.Shutdown();
CoreTiming::Shutdown();
__KernelShutdown();
HLEShutdown();
host->ShutdownSound();
Memory::Shutdown();
if (!LoadFile(filename, &coreParameter.errorString)) {
CPU_Shutdown();
coreParameter.fileToStart = "";
return false;
CPU_SetState(CPU_THREAD_NOT_RUNNING);
return;
}
if (coreParam.updateRecent)
if (coreParameter.updateRecent) {
g_Config.AddRecent(filename);
}
// Setup JIT here.
if (coreParameter.startPaused)
coreState = CORE_STEPPING;
else
coreState = CORE_RUNNING;
return true;
coreState = coreParameter.startPaused ? CORE_STEPPING : CORE_RUNNING;
}
bool PSP_IsInited()
{
return currentCPU != 0;
}
void PSP_Shutdown()
{
pspFileSystem.Shutdown();
void CPU_Shutdown() {
if (g_Config.bAutoSaveSymbolMap) {
host->SaveSymbolMap();
}
CoreTiming::Shutdown();
if (g_Config.bAutoSaveSymbolMap)
host->SaveSymbolMap();
if (coreParameter.enableSound)
{
__KernelShutdown();
HLEShutdown();
if (coreParameter.enableSound) {
host->ShutdownSound();
mixer = 0; // deleted in ShutdownSound
}
__KernelShutdown();
HLEShutdown();
pspFileSystem.Shutdown();
Memory::Shutdown();
currentCPU = 0;
}
void CPU_RunLoop() {
setCurrentThreadName("CPUThread");
if (!CPU_NextState(CPU_THREAD_PENDING, CPU_THREAD_STARTING)) {
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
return;
}
CPU_Init();
CPU_NextState(CPU_THREAD_STARTING, CPU_THREAD_RUNNING);
while (cpuThreadState != CPU_THREAD_SHUTDOWN)
{
CPU_WaitStatus(&CPU_HasPendingAction);
switch (cpuThreadState) {
case CPU_THREAD_EXECUTE:
mipsr4k.RunLoopUntil(cpuThreadUntil);
gpu->FinishEventLoop();
CPU_NextState(CPU_THREAD_EXECUTE, CPU_THREAD_RUNNING);
break;
// These are fine, just keep looping.
case CPU_THREAD_RUNNING:
case CPU_THREAD_SHUTDOWN:
break;
default:
ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState);
// Begin shutdown, otherwise we'd just spin on this bad state.
CPU_SetState(CPU_THREAD_SHUTDOWN);
break;
}
}
if (coreState != CORE_ERROR) {
coreState = CORE_POWERDOWN;
}
CPU_Shutdown();
CPU_SetState(CPU_THREAD_NOT_RUNNING);
}
void Core_UpdateState(CoreState newState) {
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
coreStatePending = true;
coreState = newState;
}
bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) {
INFO_LOG(HLE, "PPSSPP %s", PPSSPP_GIT_VERSION);
coreParameter = coreParam;
coreParameter.errorString = "";
if (g_Config.bSeparateCPUThread) {
CPU_SetState(CPU_THREAD_PENDING);
cpuThread = new std::thread(&CPU_RunLoop);
CPU_WaitStatus(&CPU_IsReady);
} else {
CPU_Init();
}
bool success = coreParameter.fileToStart != "";
*error_string = coreParameter.errorString;
if (success) {
GPU_Init();
}
return success;
}
bool PSP_IsInited() {
return currentCPU != 0;
}
void PSP_Shutdown() {
if (coreState == CORE_RUNNING)
coreState = CORE_ERROR;
if (cpuThread != NULL) {
CPU_SetState(CPU_THREAD_SHUTDOWN);
CPU_WaitStatus(&CPU_IsShutdown);
delete cpuThread;
cpuThread = 0;
} else {
CPU_Shutdown();
}
GPU_Shutdown();
}
void PSP_RunLoopUntil(u64 globalticks) {
SaveState::Process();
mipsr4k.RunLoopUntil(globalticks);
if (cpuThread != NULL) {
cpuThreadUntil = globalticks;
if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) {
// The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames.
// TODO: Something smarter? Or force CPU to bail periodically?
while (!CPU_IsReady()) {
gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(100));
}
} else {
ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);
}
} else {
mipsr4k.RunLoopUntil(globalticks);
}
}
void PSP_RunLoopFor(int cycles) {
PSP_RunLoopUntil(CoreTiming::GetTicks() + cycles);
}
CoreParameter &PSP_CoreParameter()
{
CoreParameter &PSP_CoreParameter() {
return coreParameter;
}

View file

@ -44,6 +44,8 @@ void PSP_Shutdown();
void PSP_RunLoopUntil(u64 globalticks);
void PSP_RunLoopFor(int cycles);
bool IsOnSeparateCPUThread();
void GetSysDirectories(std::string &memstickpath, std::string &flash0path);
// RUNNING must be at 0, NEXTFRAME must be at 1.

120
Core/ThreadEventQueue.h Normal file
View file

@ -0,0 +1,120 @@
// Copyright (c) 2013- 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
#include "native/base/mutex.h"
#include "Core/System.h"
#include "Core/CoreTiming.h"
#include <deque>
template <typename B, typename Event, typename EventType, EventType EVENT_INVALID, EventType EVENT_SYNC, EventType EVENT_FINISH>
struct ThreadEventQueue : public B {
void SetThreadEnabled(bool threadEnabled) {
threadEnabled_ = threadEnabled;
}
void ScheduleEvent(Event ev) {
{
lock_guard guard(eventsLock_);
events_.push_back(ev);
eventsWait_.notify_one();
}
if (!threadEnabled_) {
RunEventsUntil(0);
}
}
bool HasEvents() {
lock_guard guard(eventsLock_);
return !events_.empty();
}
Event GetNextEvent() {
lock_guard guard(eventsLock_);
if (events_.empty()) {
eventsDrain_.notify_one();
return EVENT_INVALID;
}
Event ev = events_.front();
events_.pop_front();
return ev;
}
void RunEventsUntil(u64 globalticks) {
lock_guard guard(eventsWaitLock_);
do {
for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) {
switch (EventType(ev)) {
case EVENT_FINISH:
// Stop waiting.
globalticks = 0;
break;
case EVENT_SYNC:
break;
default:
ProcessEvent(ev);
}
}
// Quit the loop if the queue is drained and coreState has tripped, or threading is disabled.
if (ShouldExitEventLoop() || !threadEnabled_) {
return;
}
// coreState changes won't wake us, so recheck periodically.
eventsWait_.wait_for(eventsWaitLock_, 1);
} while (CoreTiming::GetTicks() < globalticks);
}
void SyncThread() {
if (!threadEnabled_) {
return;
}
lock_guard guard(eventsDrainLock_);
// While processing the last event, HasEvents() will be false even while not done.
// So we schedule a nothing event and wait for that to finish.
ScheduleEvent(EVENT_SYNC);
while (HasEvents() && coreState == CORE_RUNNING) {
eventsDrain_.wait_for(eventsDrainLock_, 1);
}
}
void FinishEventLoop() {
if (threadEnabled_) {
ScheduleEvent(EVENT_FINISH);
}
}
protected:
virtual void ProcessEvent(Event ev) = 0;
virtual bool ShouldExitEventLoop() = 0;
private:
bool threadEnabled_;
std::deque<Event> events_;
recursive_mutex eventsLock_;
recursive_mutex eventsWaitLock_;
recursive_mutex eventsDrainLock_;
condition_variable eventsWait_;
condition_variable eventsDrain_;
};

View file

@ -115,7 +115,7 @@ static const u8 flushOnChangedBeforeCommandList[] = {
GE_CMD_CLUTFORMAT,
GE_CMD_TEXFILTER,
GE_CMD_TEXWRAP,
GE_CMD_TEXLEVEL,
// GE_CMD_TEXLEVEL, // we don't support this anyway, no need to flush.
GE_CMD_TEXFUNC,
GE_CMD_TEXENVCOLOR,
//GE_CMD_TEXFLUSH,
@ -234,6 +234,8 @@ void GLES_GPU::BuildReportingInfo() {
}
void GLES_GPU::DeviceLost() {
// Should only be executed on the GL thread.
// Simply drop all caches and textures.
// FBOs appear to survive? Or no?
shaderManager_->ClearCache(false);
@ -242,6 +244,10 @@ void GLES_GPU::DeviceLost() {
}
void GLES_GPU::InitClear() {
ScheduleEvent(GPU_EVENT_INIT_CLEAR);
}
void GLES_GPU::InitClearInternal() {
bool useBufferedRendering = g_Config.iRenderingMode != 0 ? 1 : 0;
if (!useBufferedRendering) {
glstate.depthWrite.set(GL_TRUE);
@ -257,6 +263,10 @@ void GLES_GPU::DumpNextFrame() {
}
void GLES_GPU::BeginFrame() {
ScheduleEvent(GPU_EVENT_BEGIN_FRAME);
}
void GLES_GPU::BeginFrameInternal() {
// Turn off vsync when unthrottled
int desiredVSyncInterval = g_Config.bVSync ? 1 : 0;
if ((PSP_CoreParameter().unthrottle) || (PSP_CoreParameter().fpsLimit == 1))
@ -294,6 +304,11 @@ void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat fo
}
bool GLES_GPU::FramebufferDirty() {
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
// Allow it to process fully before deciding if it's dirty.
SyncThread();
VirtualFramebuffer *vfb = framebufferManager_.GetDisplayFBO();
if (vfb)
return vfb->dirtyAfterDisplay;
@ -301,6 +316,10 @@ bool GLES_GPU::FramebufferDirty() {
}
void GLES_GPU::CopyDisplayToOutput() {
ScheduleEvent(GPU_EVENT_COPY_DISPLAY_TO_OUTPUT);
}
void GLES_GPU::CopyDisplayToOutputInternal() {
glstate.depthWrite.set(GL_TRUE);
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@ -340,6 +359,29 @@ void GLES_GPU::FastRunLoop(DisplayList &list) {
}
}
void GLES_GPU::ProcessEvent(GPUEvent ev) {
switch (ev.type) {
case GPU_EVENT_INIT_CLEAR:
InitClearInternal();
break;
case GPU_EVENT_BEGIN_FRAME:
BeginFrameInternal();
break;
case GPU_EVENT_COPY_DISPLAY_TO_OUTPUT:
CopyDisplayToOutputInternal();
break;
case GPU_EVENT_INVALIDATE_CACHE:
InvalidateCacheInternal(ev.invalidate_cache.addr, ev.invalidate_cache.size, ev.invalidate_cache.type);
break;
default:
GPUCommon::ProcessEvent(ev);
}
}
inline void GLES_GPU::CheckFlushOp(int cmd, u32 diff) {
if (flushBeforeCommand_[cmd] == 1 || (diff && flushBeforeCommand_[cmd] == 2))
{
@ -879,7 +921,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
shaderManager_->DirtyUniform(DIRTY_WORLDMATRIX);
}
num++;
gstate.worldmtxnum = (gstate.worldmtxnum & 0xFF000000) | (num & 0xF);
gstate.worldmtxnum = (GE_CMD_WORLDMATRIXNUMBER << 24) | (num & 0xF);
}
break;
@ -897,7 +939,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
shaderManager_->DirtyUniform(DIRTY_VIEWMATRIX);
}
num++;
gstate.viewmtxnum = (gstate.viewmtxnum & 0xFF000000) | (num & 0xF);
gstate.viewmtxnum = (GE_CMD_VIEWMATRIXNUMBER << 24) | (num & 0xF);
}
break;
@ -915,7 +957,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
shaderManager_->DirtyUniform(DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX);
}
num++;
gstate.projmtxnum = (gstate.projmtxnum & 0xFF000000) | (num & 0xF);
gstate.projmtxnum = (GE_CMD_PROJMATRIXNUMBER << 24) | (num & 0xF);
}
break;
@ -933,7 +975,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
shaderManager_->DirtyUniform(DIRTY_TEXMATRIX);
}
num++;
gstate.texmtxnum = (gstate.texmtxnum & 0xFF000000) | (num & 0xF);
gstate.texmtxnum = (GE_CMD_TGENMATRIXNUMBER << 24) | (num & 0xF);
}
break;
@ -951,7 +993,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
shaderManager_->DirtyUniform(DIRTY_BONEMATRIX0 << (num / 12));
}
num++;
gstate.boneMatrixNumber = (gstate.boneMatrixNumber & 0xFF000000) | (num & 0x7F);
gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F);
}
break;
@ -1053,6 +1095,14 @@ void GLES_GPU::DoBlockTransfer() {
}
void GLES_GPU::InvalidateCache(u32 addr, int size, GPUInvalidationType type) {
GPUEvent ev(GPU_EVENT_INVALIDATE_CACHE);
ev.invalidate_cache.addr = addr;
ev.invalidate_cache.size = size;
ev.invalidate_cache.type = type;
ScheduleEvent(ev);
}
void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type) {
if (size > 0)
textureCache_.Invalidate(addr, size, type);
else
@ -1070,11 +1120,6 @@ void GLES_GPU::ClearCacheNextFrame() {
textureCache_.ClearNextFrame();
}
void GLES_GPU::Flush() {
transformDraw_.Flush();
}
void GLES_GPU::Resized() {
framebufferManager_.Resized();
}

View file

@ -49,13 +49,11 @@ public:
virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders.
virtual void DumpNextFrame();
virtual void Flush();
virtual void DoState(PointerWrap &p);
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.
virtual void Resized();
virtual bool DecodeTexture(u8* dest, GPUgstate state)
{
virtual bool DecodeTexture(u8* dest, GPUgstate state) {
return textureCache_.DecodeTexture(dest, state);
}
virtual bool FramebufferDirty();
@ -68,12 +66,20 @@ public:
protected:
virtual void FastRunLoop(DisplayList &list);
virtual void ProcessEvent(GPUEvent ev);
private:
void Flush() {
transformDraw_.Flush();
}
void DoBlockTransfer();
void ApplyDrawState(int prim);
void CheckFlushOp(int cmd, u32 diff);
void BuildReportingInfo();
void InitClearInternal();
void BeginFrameInternal();
void CopyDisplayToOutputInternal();
void InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type);
FramebufferManager framebufferManager_;
TextureCache textureCache_;

View file

@ -299,6 +299,10 @@ void FramebufferManager::DrawPixels(const u8 *framebuf, GEBufferFormat pixelForm
memcpy(dst, src, 4 * 480);
}
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
}
}
}

View file

@ -446,7 +446,7 @@ void LinkedShader::updateUniforms() {
dirtyUniforms = 0;
}
ShaderManager::ShaderManager() : lastShader(NULL), globalDirty(0xFFFFFFFF), shaderSwitchDirty(0) {
ShaderManager::ShaderManager() : lastShader_(NULL), globalDirty_(0xFFFFFFFF), shaderSwitchDirty_(0) {
codeBuffer_ = new char[16384];
}
@ -454,27 +454,22 @@ ShaderManager::~ShaderManager() {
delete [] codeBuffer_;
}
void ShaderManager::DirtyUniform(u32 what) {
globalDirty |= what;
}
void ShaderManager::Clear() {
for (auto iter = linkedShaderCache.begin(); iter != linkedShaderCache.end(); ++iter) {
for (auto iter = linkedShaderCache_.begin(); iter != linkedShaderCache_.end(); ++iter) {
delete iter->ls;
}
for (auto iter = fsCache.begin(); iter != fsCache.end(); ++iter) {
for (auto iter = fsCache_.begin(); iter != fsCache_.end(); ++iter) {
delete iter->second;
}
for (auto iter = vsCache.begin(); iter != vsCache.end(); ++iter) {
for (auto iter = vsCache_.begin(); iter != vsCache_.end(); ++iter) {
delete iter->second;
}
linkedShaderCache.clear();
fsCache.clear();
vsCache.clear();
globalDirty = 0xFFFFFFFF;
lastFSID.clear();
lastVSID.clear();
linkedShaderCache_.clear();
fsCache_.clear();
vsCache_.clear();
globalDirty_ = 0xFFFFFFFF;
lastFSID_.clear();
lastVSID_.clear();
DirtyShader();
}
@ -482,29 +477,28 @@ void ShaderManager::ClearCache(bool deleteThem) {
Clear();
}
void ShaderManager::DirtyShader() {
// Forget the last shader ID
lastFSID.clear();
lastVSID.clear();
lastShader = 0;
globalDirty = 0xFFFFFFFF;
shaderSwitchDirty = 0;
lastFSID_.clear();
lastVSID_.clear();
lastShader_ = 0;
globalDirty_ = 0xFFFFFFFF;
shaderSwitchDirty_ = 0;
}
void ShaderManager::EndFrame() { // disables vertex arrays
if (lastShader)
lastShader->stop();
lastShader = 0;
if (lastShader_)
lastShader_->stop();
lastShader_ = 0;
}
LinkedShader *ShaderManager::ApplyShader(int prim) {
if (globalDirty) {
if (lastShader)
lastShader->dirtyUniforms |= globalDirty;
shaderSwitchDirty |= globalDirty;
globalDirty = 0;
if (globalDirty_) {
if (lastShader_)
lastShader_->dirtyUniforms |= globalDirty_;
shaderSwitchDirty_ |= globalDirty_;
globalDirty_ = 0;
}
bool useHWTransform = CanUseHardwareTransform(prim);
@ -515,22 +509,22 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
ComputeFragmentShaderID(&FSID);
// Just update uniforms if this is the same shader as last time.
if (lastShader != 0 && VSID == lastVSID && FSID == lastFSID) {
lastShader->updateUniforms();
return lastShader; // Already all set.
if (lastShader_ != 0 && VSID == lastVSID_ && FSID == lastFSID_) {
lastShader_->updateUniforms();
return lastShader_; // Already all set.
}
if (lastShader != 0) {
if (lastShader_ != 0) {
// There was a previous shader and we're switching.
lastShader->stop();
lastShader_->stop();
}
lastVSID = VSID;
lastFSID = FSID;
lastVSID_ = VSID;
lastFSID_ = FSID;
VSCache::iterator vsIter = vsCache.find(VSID);
VSCache::iterator vsIter = vsCache_.find(VSID);
Shader *vs;
if (vsIter == vsCache.end()) {
if (vsIter == vsCache_.end()) {
// Vertex shader not in cache. Let's compile it.
GenerateVertexShader(prim, codeBuffer_, useHWTransform);
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER, useHWTransform);
@ -549,18 +543,18 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER, false);
}
vsCache[VSID] = vs;
vsCache_[VSID] = vs;
} else {
vs = vsIter->second;
}
FSCache::iterator fsIter = fsCache.find(FSID);
FSCache::iterator fsIter = fsCache_.find(FSID);
Shader *fs;
if (fsIter == fsCache.end()) {
if (fsIter == fsCache_.end()) {
// Fragment shader not in cache. Let's compile it.
GenerateFragmentShader(codeBuffer_);
fs = new Shader(codeBuffer_, GL_FRAGMENT_SHADER, useHWTransform);
fsCache[FSID] = fs;
fsCache_[FSID] = fs;
} else {
fs = fsIter->second;
}
@ -568,24 +562,24 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
// Okay, we have both shaders. Let's see if there's a linked one.
LinkedShader *ls = NULL;
for (auto iter = linkedShaderCache.begin(); iter != linkedShaderCache.end(); ++iter) {
for (auto iter = linkedShaderCache_.begin(); iter != linkedShaderCache_.end(); ++iter) {
// Deferred dirtying! Let's see if we can make this even more clever later.
iter->ls->dirtyUniforms |= shaderSwitchDirty;
iter->ls->dirtyUniforms |= shaderSwitchDirty_;
if (iter->vs == vs && iter->fs == fs) {
ls = iter->ls;
}
}
shaderSwitchDirty = 0;
shaderSwitchDirty_ = 0;
if (ls == NULL) {
ls = new LinkedShader(vs, fs, vs->UseHWTransform()); // This does "use" automatically
const LinkedShaderCacheEntry entry(vs, fs, ls);
linkedShaderCache.push_back(entry);
linkedShaderCache_.push_back(entry);
} else {
ls->use();
}
lastShader = ls;
lastShader_ = ls;
return ls;
}

View file

@ -156,12 +156,14 @@ public:
void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected
LinkedShader *ApplyShader(int prim);
void DirtyShader();
void DirtyUniform(u32 what);
void DirtyUniform(u32 what) {
globalDirty_ |= what;
}
void EndFrame(); // disables vertex arrays
int NumVertexShaders() const { return (int)vsCache.size(); }
int NumFragmentShaders() const { return (int)fsCache.size(); }
int NumPrograms() const { return (int)linkedShaderCache.size(); }
int NumVertexShaders() const { return (int)vsCache_.size(); }
int NumFragmentShaders() const { return (int)fsCache_.size(); }
int NumPrograms() const { return (int)linkedShaderCache_.size(); }
private:
void Clear();
@ -176,18 +178,18 @@ private:
};
typedef std::vector<LinkedShaderCacheEntry> LinkedShaderCache;
LinkedShaderCache linkedShaderCache;
FragmentShaderID lastFSID;
VertexShaderID lastVSID;
LinkedShaderCache linkedShaderCache_;
FragmentShaderID lastFSID_;
VertexShaderID lastVSID_;
LinkedShader *lastShader;
u32 globalDirty;
u32 shaderSwitchDirty;
LinkedShader *lastShader_;
u32 globalDirty_;
u32 shaderSwitchDirty_;
char *codeBuffer_;
typedef std::map<FragmentShaderID, Shader *> FSCache;
FSCache fsCache;
FSCache fsCache_;
typedef std::map<VertexShaderID, Shader *> VSCache;
VSCache vsCache;
VSCache vsCache_;
};

View file

@ -254,10 +254,6 @@ void TextureCache::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffe
}
}
static u32 GetClutAddr() {
return ((gstate.clutaddr & 0xFFFFFF) | ((gstate.clutaddrupper << 8) & 0x0F000000));
}
static u32 GetClutIndex(u32 index) {
const u32 clutBase = gstate.getClutIndexStartPos();
const u32 clutMask = gstate.getClutIndexMask();
@ -276,9 +272,9 @@ void *TextureCache::UnswizzleFromMem(u32 texaddr, u32 bufw, u32 bytesPerPixel, u
u32 ydest = 0;
if (rowWidth >= 16) {
const u32 *src = (u32 *) Memory::GetPointer(texaddr);
u32 *ydest = tmpTexBuf32.data();
u32 *ydestp = tmpTexBuf32.data();
for (int by = 0; by < byc; by++) {
u32 *xdest = ydest;
u32 *xdest = ydestp;
for (int bx = 0; bx < bxc; bx++) {
u32 *dest = xdest;
for (int n = 0; n < 8; n++) {
@ -288,7 +284,7 @@ void *TextureCache::UnswizzleFromMem(u32 texaddr, u32 bufw, u32 bytesPerPixel, u
}
xdest += 4;
}
ydest += (rowWidth * 8) / 4;
ydestp += (rowWidth * 8) / 4;
}
} else if (rowWidth == 8) {
const u32 *src = (u32 *) Memory::GetPointer(texaddr);
@ -877,10 +873,10 @@ inline bool TextureCache::TexCacheEntry::Matches(u16 dim2, u8 format2, int maxLe
}
void TextureCache::LoadClut() {
u32 clutAddr = GetClutAddr();
u32 clutAddr = ((gstate.clutaddr & 0xFFFFFF) | ((gstate.clutaddrupper << 8) & 0x0F000000));
clutTotalBytes_ = (gstate.loadclut & 0x3f) * 32;
if (Memory::IsValidAddress(clutAddr)) {
Memory::Memcpy(clutBufRaw_, clutAddr, clutTotalBytes_);
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, clutTotalBytes_);
} else {
memset(clutBufRaw_, 0xFF, clutTotalBytes_);
}

View file

@ -1109,10 +1109,7 @@ VertexArrayInfo::~VertexArrayInfo() {
glDeleteBuffers(1, &ebo);
}
void TransformDrawEngine::Flush() {
if (!numDrawCalls)
return;
void TransformDrawEngine::DoFlush() {
gpuStats.numFlushes++;
gpuStats.numTrackedVertexArrays = (int)vai_.size();

View file

@ -100,7 +100,6 @@ public:
void DrawBezier(int ucount, int vcount);
void DrawSpline(int ucount, int vcount, int utype, int vtype);
void DecodeVerts();
void Flush();
void SetShaderManager(ShaderManager *shaderManager) {
shaderManager_ = shaderManager;
}
@ -122,7 +121,15 @@ public:
// This requires a SetupVertexDecoder call first.
int EstimatePerVertexCost();
// So that this can be inlined
void Flush() {
if (!numDrawCalls)
return;
DoFlush();
}
private:
void DoFlush();
void SoftwareTransformAndDraw(int prim, u8 *decoded, LinkedShader *program, int vertexCount, u32 vertexType, void *inds, int indexType, const DecVtxFormat &decVtxFormat, int maxIndex);
void ApplyDrawState(int prim);
bool IsReallyAClear(int numVerts) const;

View file

@ -1,5 +1,6 @@
#include <algorithm>
#include "base/timeutil.h"
#include "native/base/mutex.h"
#include "native/base/timeutil.h"
#include "GeDisasm.h"
#include "GPUCommon.h"
#include "GPUState.h"
@ -9,9 +10,7 @@
#include "Core/MemMap.h"
#include "Core/Host.h"
#include "Core/Reporting.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceGe.h"
GPUCommon::GPUCommon() :
@ -21,16 +20,19 @@ GPUCommon::GPUCommon() :
busyTicks(0),
dumpNextFrame_(false),
dumpThisFrame_(false),
interruptsEnabled_(true)
interruptsEnabled_(true),
curTickEst_(0)
{
memset(dls, 0, sizeof(dls));
for (int i = 0; i < DisplayListMaxCount; ++i) {
dls[i].state = PSP_GE_DL_STATE_NONE;
dls[i].waitTicks = 0;
}
SetThreadEnabled(g_Config.bSeparateCPUThread);
}
void GPUCommon::PopDLQueue() {
easy_guard guard(listLock);
if(!dlQueue.empty()) {
dlQueue.pop_front();
if(!dlQueue.empty()) {
@ -45,13 +47,19 @@ void GPUCommon::PopDLQueue() {
}
u32 GPUCommon::DrawSync(int mode) {
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
// Sync first, because the CPU is usually faster than the emulated GPU.
SyncThread();
easy_guard guard(listLock);
if (mode < 0 || mode > 1)
return SCE_KERNEL_ERROR_INVALID_MODE;
if (mode == 0) {
// TODO: What if dispatch / interrupts disabled?
if (drawCompleteTicks > CoreTiming::GetTicks()) {
__KernelWaitCurThread(WAITTYPE_GEDRAWSYNC, 1, 0, 0, false, "GeDrawSync");
__GeWaitCurrentThread(WAITTYPE_GEDRAWSYNC, 1, "GeDrawSync");
} else {
for (int i = 0; i < DisplayListMaxCount; ++i) {
if (dls[i].state == PSP_GE_DL_STATE_COMPLETED) {
@ -79,16 +87,21 @@ u32 GPUCommon::DrawSync(int mode) {
return PSP_GE_LIST_DRAWING;
}
void GPUCommon::CheckDrawSync()
{
void GPUCommon::CheckDrawSync() {
easy_guard guard(listLock);
if (dlQueue.empty()) {
for (int i = 0; i < DisplayListMaxCount; ++i)
dls[i].state = PSP_GE_DL_STATE_NONE;
}
}
int GPUCommon::ListSync(int listid, int mode)
{
int GPUCommon::ListSync(int listid, int mode) {
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
// Sync first, because the CPU is usually faster than the emulated GPU.
SyncThread();
easy_guard guard(listLock);
if (listid < 0 || listid >= DisplayListMaxCount)
return SCE_KERNEL_ERROR_INVALID_ID;
@ -120,13 +133,13 @@ int GPUCommon::ListSync(int listid, int mode)
}
if (dl.waitTicks > CoreTiming::GetTicks()) {
__KernelWaitCurThread(WAITTYPE_GELISTSYNC, listid, 0, 0, false, "GeListSync");
__GeWaitCurrentThread(WAITTYPE_GELISTSYNC, listid, "GeListSync");
}
return PSP_GE_LIST_COMPLETED;
}
u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head)
{
u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head) {
easy_guard guard(listLock);
// TODO Check the stack values in missing arg and ajust the stack depth
// Check alignment
@ -210,14 +223,15 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head)
drawCompleteTicks = (u64)-1;
// TODO save context when starting the list if param is set
guard.unlock();
ProcessDLQueue();
}
return id;
}
u32 GPUCommon::DequeueList(int listid)
{
u32 GPUCommon::DequeueList(int listid) {
easy_guard guard(listLock);
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
return SCE_KERNEL_ERROR_INVALID_ID;
@ -232,15 +246,15 @@ u32 GPUCommon::DequeueList(int listid)
dlQueue.remove(listid);
dls[listid].waitTicks = 0;
__KernelTriggerWait(WAITTYPE_GELISTSYNC, listid, 0, "GeListSync");
__GeTriggerWait(WAITTYPE_GELISTSYNC, listid, "GeListSync");
CheckDrawSync();
return 0;
}
u32 GPUCommon::UpdateStall(int listid, u32 newstall)
{
u32 GPUCommon::UpdateStall(int listid, u32 newstall) {
easy_guard guard(listLock);
if (listid < 0 || listid >= DisplayListMaxCount || dls[listid].state == PSP_GE_DL_STATE_NONE)
return SCE_KERNEL_ERROR_INVALID_ID;
@ -249,13 +263,14 @@ u32 GPUCommon::UpdateStall(int listid, u32 newstall)
if (dls[listid].signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
dls[listid].signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
guard.unlock();
ProcessDLQueue();
return 0;
}
u32 GPUCommon::Continue()
{
u32 GPUCommon::Continue() {
easy_guard guard(listLock);
if (!currentList)
return 0;
@ -291,12 +306,13 @@ u32 GPUCommon::Continue()
return -1;
}
guard.unlock();
ProcessDLQueue();
return 0;
}
u32 GPUCommon::Break(int mode)
{
u32 GPUCommon::Break(int mode) {
easy_guard guard(listLock);
if (mode < 0 || mode > 1)
return SCE_KERNEL_ERROR_INVALID_MODE;
@ -359,20 +375,19 @@ u32 GPUCommon::Break(int mode)
return currentList->id;
}
bool GPUCommon::InterpretList(DisplayList &list)
{
bool GPUCommon::InterpretList(DisplayList &list) {
// Initialized to avoid a race condition with bShowDebugStats changing.
double start = 0.0;
if (g_Config.bShowDebugStats)
{
if (g_Config.bShowDebugStats) {
time_update();
start = time_now_d();
}
easy_guard guard(listLock);
// TODO: This has to be right... but it freezes right now?
//if (list.state == PSP_GE_DL_STATE_PAUSED)
// return false;
currentList = &list;
// I don't know if this is the correct place to zero this, but something
@ -384,9 +399,9 @@ bool GPUCommon::InterpretList(DisplayList &list)
ERROR_LOG_REPORT(G3D, "DL PC = %08x WTF!!!!", list.pc);
return true;
}
#if defined(USING_QT_UI)
if(host->GpuStep())
{
if (host->GpuStep()) {
host->SendGPUStart();
}
#endif
@ -397,32 +412,43 @@ bool GPUCommon::InterpretList(DisplayList &list)
list.interrupted = false;
gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
guard.unlock();
const bool dumpThisFrame = dumpThisFrame_;
// TODO: Add check for displaylist debugger.
const bool useFastRunLoop = !dumpThisFrame;
while (gpuState == GPUSTATE_RUNNING)
{
if (list.pc == list.stall)
while (gpuState == GPUSTATE_RUNNING) {
{
gpuState = GPUSTATE_STALL;
downcount = 0;
easy_guard innerGuard(listLock);
if (list.pc == list.stall) {
gpuState = GPUSTATE_STALL;
downcount = 0;
}
}
if (useFastRunLoop)
if (useFastRunLoop) {
FastRunLoop(list);
else
} else {
SlowRunLoop(list);
}
downcount = list.stall == 0 ? 0xFFFFFFF : (list.stall - list.pc) / 4;
{
easy_guard innerGuard(listLock);
downcount = list.stall == 0 ? 0xFFFFFFF : (list.stall - list.pc) / 4;
if (gpuState == GPUSTATE_STALL && list.stall != list.pc) {
// Unstalled.
gpuState = GPUSTATE_RUNNING;
}
}
}
// We haven't run the op at list.pc, so it shouldn't count.
if (cycleLastPC != list.pc)
if (cycleLastPC != list.pc) {
UpdatePC(list.pc - 4, list.pc);
}
if (g_Config.bShowDebugStats)
{
if (g_Config.bShowDebugStats) {
time_update();
gpuStats.msProcessingDisplayLists += time_now_d() - start;
}
@ -460,8 +486,7 @@ void GPUCommon::SlowRunLoop(DisplayList &list)
}
// The newPC parameter is used for jumps, we don't count cycles between.
inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC)
{
inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC) {
// Rough estimate, 2 CPU ticks (it's double the clock rate) per GPU instruction.
cyclesExecuted += 2 * (currentPC - cycleLastPC) / 4;
gpuStats.otherGPUCycles += 2 * (currentPC - cycleLastPC) / 4;
@ -471,8 +496,15 @@ inline void GPUCommon::UpdatePC(u32 currentPC, u32 newPC)
downcount = 0;
}
void GPUCommon::ReapplyGfxState()
{
void GPUCommon::ReapplyGfxState() {
if (IsOnSeparateCPUThread()) {
ScheduleEvent(GPU_EVENT_REAPPLY_GFX_STATE);
} else {
ReapplyGfxStateInternal();
}
}
void GPUCommon::ReapplyGfxStateInternal() {
// ShaderManager_DirtyShader();
// The commands are embedded in the command memory so we can just reexecute the words. Convenient.
// To be safe we pass 0xFFFFFFF as the diff.
@ -488,37 +520,50 @@ void GPUCommon::ReapplyGfxState()
ExecuteOp(gstate.cmdmem[GE_CMD_SCISSOR2], 0xFFFFFFFF);
*/
for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++)
{
if (i != GE_CMD_ORIGIN)
for (int i = GE_CMD_VERTEXTYPE; i < GE_CMD_BONEMATRIXNUMBER; i++) {
if (i != GE_CMD_ORIGIN) {
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
}
}
// Can't write to bonematrixnumber here
for (int i = GE_CMD_MORPHWEIGHT0; i < GE_CMD_PATCHFACING; i++)
{
for (int i = GE_CMD_MORPHWEIGHT0; i < GE_CMD_PATCHFACING; i++) {
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
}
// There are a few here in the middle that we shouldn't execute...
for (int i = GE_CMD_VIEWPORTX1; i < GE_CMD_TRANSFERSTART; i++)
{
for (int i = GE_CMD_VIEWPORTX1; i < GE_CMD_TRANSFERSTART; i++) {
ExecuteOp(gstate.cmdmem[i], 0xFFFFFFFF);
}
// TODO: there's more...
}
inline void GPUCommon::UpdateState(GPUState state)
{
inline void GPUCommon::UpdateState(GPUState state) {
gpuState = state;
if (state != GPUSTATE_RUNNING)
downcount = 0;
}
void GPUCommon::ProcessEvent(GPUEvent ev) {
switch (ev.type) {
case GPU_EVENT_PROCESS_QUEUE:
ProcessDLQueueInternal();
break;
case GPU_EVENT_REAPPLY_GFX_STATE:
ReapplyGfxStateInternal();
break;
default:
ERROR_LOG(G3D, "Unexpected GPU event type: %d", (int)ev);
}
}
int GPUCommon::GetNextListIndex() {
easy_guard guard(listLock);
auto iter = dlQueue.begin();
if (iter != dlQueue.end()) {
return *iter;
@ -528,32 +573,41 @@ int GPUCommon::GetNextListIndex() {
}
bool GPUCommon::ProcessDLQueue() {
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
return true;
}
void GPUCommon::ProcessDLQueueInternal() {
startingTicks = CoreTiming::GetTicks();
cyclesExecuted = 0;
UpdateTickEstimate(std::max(busyTicks, startingTicks + cyclesExecuted));
if (startingTicks < busyTicks) {
DEBUG_LOG(HLE, "Can't execute a list yet, still busy for %lld ticks", busyTicks - startingTicks);
return false;
return;
}
for (int listIndex = GetNextListIndex(); listIndex != -1; listIndex = GetNextListIndex()) {
DisplayList &l = dls[listIndex];
DEBUG_LOG(G3D, "Okay, starting DL execution at %08x - stall = %08x", l.pc, l.stall);
if (!InterpretList(l)) {
return false;
return;
} else {
easy_guard guard(listLock);
// At the end, we can remove it from the queue and continue.
dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end());
UpdateTickEstimate(std::max(busyTicks, startingTicks + cyclesExecuted));
}
}
easy_guard guard(listLock);
currentList = NULL;
drawCompleteTicks = startingTicks + cyclesExecuted;
busyTicks = std::max(busyTicks, drawCompleteTicks);
__GeTriggerSync(WAITTYPE_GEDRAWSYNC, 1, drawCompleteTicks);
return true; //no more lists!
// Since the event is in CoreTiming, we're in sync. Just set 0 now.
UpdateTickEstimate(0);
}
void GPUCommon::PreExecuteOp(u32 op, u32 diff) {
@ -574,11 +628,15 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
break;
case GE_CMD_ORIGIN:
gstate_c.offsetAddr = currentList->pc;
{
easy_guard guard(listLock);
gstate_c.offsetAddr = currentList->pc;
}
break;
case GE_CMD_JUMP:
{
easy_guard guard(listLock);
u32 target = gstate_c.getRelativeAddress(data);
if (Memory::IsValidAddress(target)) {
UpdatePC(currentList->pc, target - 4);
@ -591,6 +649,7 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_CALL:
{
easy_guard guard(listLock);
// Saint Seiya needs correct support for relative calls.
u32 retval = currentList->pc + 4;
u32 target = gstate_c.getRelativeAddress(data);
@ -610,6 +669,7 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_RET:
{
easy_guard guard(listLock);
if (currentList->stackptr == 0) {
ERROR_LOG_REPORT(G3D, "RET: Stack empty!");
} else {
@ -632,6 +692,7 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
break;
case GE_CMD_END: {
easy_guard guard(listLock);
u32 prev = Memory::ReadUnchecked_U32(currentList->pc - 4);
UpdatePC(currentList->pc);
switch (prev >> 24) {
@ -767,6 +828,8 @@ void GPUCommon::ExecuteOp(u32 op, u32 diff) {
}
void GPUCommon::DoState(PointerWrap &p) {
easy_guard guard(listLock);
p.Do<int>(dlQueue);
p.DoArray(dls, ARRAY_SIZE(dls));
int currentID = 0;
@ -790,12 +853,11 @@ void GPUCommon::DoState(PointerWrap &p) {
p.DoMarker("GPUCommon");
}
void GPUCommon::InterruptStart(int listid)
{
void GPUCommon::InterruptStart(int listid) {
interruptRunning = true;
}
void GPUCommon::InterruptEnd(int listid)
{
void GPUCommon::InterruptEnd(int listid) {
easy_guard guard(listLock);
interruptRunning = false;
isbreak = false;
@ -803,18 +865,19 @@ void GPUCommon::InterruptEnd(int listid)
// TODO: Unless the signal handler could change it?
if (dl.state == PSP_GE_DL_STATE_COMPLETED || dl.state == PSP_GE_DL_STATE_NONE) {
dl.waitTicks = 0;
__KernelTriggerWait(WAITTYPE_GELISTSYNC, listid, 0, "GeListSync", true);
__GeTriggerWait(WAITTYPE_GELISTSYNC, listid, "GeListSync", true);
}
if (dl.signal == PSP_GE_SIGNAL_HANDLER_PAUSE)
dl.signal = PSP_GE_SIGNAL_HANDLER_SUSPEND;
guard.unlock();
ProcessDLQueue();
}
// TODO: Maybe cleaner to keep this in GE and trigger the clear directly?
void GPUCommon::SyncEnd(WaitType waitType, int listid, bool wokeThreads)
{
void GPUCommon::SyncEnd(WaitType waitType, int listid, bool wokeThreads) {
easy_guard guard(listLock);
if (waitType == WAITTYPE_GEDRAWSYNC && wokeThreads)
{
for (int i = 0; i < DisplayListMaxCount; ++i) {

View file

@ -1,8 +1,18 @@
#pragma once
#include "GPUInterface.h"
#include "Common/Common.h"
#include "Core/ThreadEventQueue.h"
#include "GPU/GPUInterface.h"
class GPUCommon : public GPUInterface
#if defined(ANDROID)
#include <atomic>
#elif defined(_M_SSE)
#include <xmmintrin.h>
#endif
typedef ThreadEventQueue<GPUInterface, GPUEvent, GPUEventType, GPU_EVENT_INVALID, GPU_EVENT_SYNC_THREAD, GPU_EVENT_FINISH_EVENT_LOOP> GPUThreadEventQueue;
class GPUCommon : public GPUThreadEventQueue
{
public:
GPUCommon();
@ -25,11 +35,28 @@ public:
virtual int ListSync(int listid, int mode);
virtual u32 DrawSync(int mode);
virtual void DoState(PointerWrap &p);
virtual bool FramebufferDirty() { return true; }
virtual bool FramebufferDirty() {
SyncThread();
return true;
}
virtual u32 Continue();
virtual u32 Break(int mode);
virtual void ReapplyGfxState();
virtual u64 GetTickEstimate() {
#if defined(_M_X64) || defined(ANDROID)
return curTickEst_;
#elif defined(_M_SSE)
__m64 result = *(__m64 *)&curTickEst_;
u64 safeResult = *(u64 *)&result;
_mm_empty();
return safeResult;
#else
lock_guard guard(curTickEstLock_);
return curTickEst_;
#endif
}
protected:
// To avoid virtual calls to PreExecuteOp().
virtual void FastRunLoop(DisplayList &list) = 0;
@ -39,12 +66,31 @@ protected:
void PopDLQueue();
void CheckDrawSync();
int GetNextListIndex();
void ProcessDLQueueInternal();
void ReapplyGfxStateInternal();
virtual void ProcessEvent(GPUEvent ev);
virtual bool ShouldExitEventLoop() {
return coreState != CORE_RUNNING;
}
// Allows early unlocking with a guard. Do not double unlock.
class easy_guard {
public:
easy_guard(recursive_mutex &mtx) : mtx_(mtx), locked_(true) { mtx_.lock(); }
~easy_guard() { if (locked_) mtx_.unlock(); }
void unlock() { if (locked_) mtx_.unlock(); else Crash(); locked_ = false; }
private:
recursive_mutex &mtx_;
bool locked_;
};
typedef std::list<int> DisplayListQueue;
DisplayList dls[DisplayListMaxCount];
DisplayList *currentList;
DisplayListQueue dlQueue;
recursive_mutex listLock;
bool interruptRunning;
GPUState gpuState;
@ -61,6 +107,27 @@ protected:
bool dumpThisFrame_;
bool interruptsEnabled_;
// For CPU/GPU sync.
#ifdef ANDROID
std::atomic<u64> curTickEst_;
#else
volatile MEMORY_ALIGNED16(u64) curTickEst_;
recursive_mutex curTickEstLock_;
#endif
virtual void UpdateTickEstimate(u64 value) {
#if defined(_M_X64) || defined(ANDROID)
curTickEst_ = value;
#elif defined(_M_SSE)
__m64 result = *(__m64 *)&value;
*(__m64 *)&curTickEst_ = result;
_mm_empty();
#else
lock_guard guard(curTickEstLock_);
curTickEst_ = value;
#endif
}
public:
virtual DisplayList* getList(int listid)
{

View file

@ -140,6 +140,35 @@ enum GPUInvalidationType {
GPU_INVALIDATE_SAFE,
};
enum GPUEventType {
GPU_EVENT_INVALID,
GPU_EVENT_PROCESS_QUEUE,
GPU_EVENT_INIT_CLEAR,
GPU_EVENT_BEGIN_FRAME,
GPU_EVENT_COPY_DISPLAY_TO_OUTPUT,
GPU_EVENT_REAPPLY_GFX_STATE,
GPU_EVENT_INVALIDATE_CACHE,
GPU_EVENT_FINISH_EVENT_LOOP,
GPU_EVENT_SYNC_THREAD,
};
struct GPUEvent {
GPUEvent(GPUEventType t) : type(t) {}
GPUEventType type;
union {
// GPU_EVENT_INVALIDATE_CACHE
struct {
u32 addr;
int size;
GPUInvalidationType type;
} invalidate_cache;
};
operator GPUEventType() const {
return type;
}
};
class GPUInterface
{
public:
@ -150,6 +179,9 @@ public:
// Initialization
virtual void InitClear() = 0;
virtual void RunEventsUntil(u64 globalticks) = 0;
virtual void FinishEventLoop() = 0;
// Draw queue management
virtual DisplayList* getList(int listid) = 0;
// TODO: Much of this should probably be shared between the different GPU implementations.
@ -190,8 +222,9 @@ public:
virtual void EnableInterrupts(bool enable) = 0;
virtual void DeviceLost() = 0;
virtual void Flush() = 0;
virtual void ReapplyGfxState() = 0;
virtual void SyncThread() = 0;
virtual u64 GetTickEstimate() = 0;
virtual void DoState(PointerWrap &p) = 0;
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.

View file

@ -28,6 +28,25 @@ GPUStateCache gstate_c;
GPUInterface *gpu;
GPUStatistics gpuStats;
void GPU_Init() {
switch (PSP_CoreParameter().gpuCore) {
case GPU_NULL:
gpu = new NullGPU();
break;
case GPU_GLES:
gpu = new GLES_GPU();
break;
case GPU_SOFTWARE:
gpu = new NullGPU();
break;
}
}
void GPU_Shutdown() {
delete gpu;
gpu = 0;
}
void InitGfxState()
{
memset(&gstate, 0, sizeof(gstate));
@ -56,24 +75,10 @@ void InitGfxState()
for (int i = 0; i < 8; i++) {
memcpy(gstate.boneMatrix + i * 12, identity4x3, 12 * sizeof(float));
}
switch (PSP_CoreParameter().gpuCore) {
case GPU_NULL:
gpu = new NullGPU();
break;
case GPU_GLES:
gpu = new GLES_GPU();
break;
case GPU_SOFTWARE:
gpu = new NullGPU();
break;
}
}
void ShutdownGfxState()
{
delete gpu;
gpu = NULL;
}
// When you have changed state outside the psp gfx core,

View file

@ -446,6 +446,9 @@ struct GPUStatistics {
int numFBOs;
};
void GPU_Init();
void GPU_Shutdown();
void InitGfxState();
void ShutdownGfxState();
void ReapplyGfxState();

View file

@ -896,9 +896,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer) {
case GE_CMD_TEXLEVEL:
if (data & ~0xFF0003)
sprintf(buffer, "TexWrap mode: %i Offset: %i (extra %x)", data&3, data >> 16, data);
sprintf(buffer, "TexLevel mode: %i Offset: %i (extra %x)", data&3, data >> 16, data);
else
sprintf(buffer, "TexWrap mode: %i Offset: %i", data&3, data >> 16);
sprintf(buffer, "TexLevel mode: %i Offset: %i", data&3, data >> 16);
break;
case GE_CMD_FOG1:

View file

@ -37,7 +37,6 @@ public:
virtual void InvalidateCache(u32 addr, int size, GPUInvalidationType type);
virtual void UpdateMemory(u32 dest, u32 src, int size);
virtual void ClearCacheNextFrame() {};
virtual void Flush() {}
virtual void DeviceLost() {}
virtual void DumpNextFrame() {}

View file

@ -58,7 +58,7 @@ public:
std::string title; // for easy access, also available in paramSFO.
std::string id;
std::string id_version;
EmuFileType fileType;
IdentifiedFileType fileType;
ParamSFOData paramSFO;
bool paramSFOLoaded;

View file

@ -100,6 +100,7 @@ void GameScreen::DrawBackground(UIContext &dc) {
void GameScreen::update(InputState &input) {
UIScreen::update(input);
I18NCategory *g = GetI18NCategory("General");
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true);
if (tvTitle_)
@ -112,9 +113,9 @@ void GameScreen::update(InputState &input) {
if (info->gameSize) {
char temp[256];
sprintf(temp, "Game: %1.1f MB", (float)(info->gameSize) / 1024.f / 1024.f);
sprintf(temp, "%s: %1.1f %s", g->T("Game"), (float) (info->gameSize) / 1024.f / 1024.f, g->T("MB"));
tvGameSize_->SetText(temp);
sprintf(temp, "SaveData: %1.2f MB", (float)(info->saveDataSize) / 1024.f / 1024.f);
sprintf(temp, "%s: %1.2f %s", g->T("SaveData"), (float) (info->saveDataSize) / 1024.f / 1024.f, g->T("MB"));
tvSaveDataSize_->SetText(temp);
}
}
@ -139,10 +140,12 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) {
}
UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) {
I18NCategory *d = GetI18NCategory("Dialog");
I18NCategory *g = GetI18NCategory("General");
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true);
if (info) {
screenManager()->push(
new PromptScreen("Do you really want to delete all\nyour save data for this game?", "Delete Savedata", "Cancel",
new PromptScreen(d->T("DeleteConfirmAll", "Do you really want to delete all\nyour save data for this game?"), g->T("Delete Savedata"), g->T("Cancel"),
std::bind(&GameScreen::CallbackDeleteSaveData, this, placeholder::_1)));
}
@ -158,10 +161,12 @@ void GameScreen::CallbackDeleteSaveData(bool yes) {
}
UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) {
I18NCategory *d = GetI18NCategory("Dialog");
I18NCategory *g = GetI18NCategory("General");
GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true);
if (info) {
screenManager()->push(
new PromptScreen("Do you really want to delete all\nthis game entirely? You can't undo this.", "Delete Game", "Cancel",
new PromptScreen(d->T("DeleteGame", "Do you really want to delete all\nthis game entirely? You can't undo this."), g->T("Delete Game"), g->T("Cancel"),
std::bind(&GameScreen::CallbackDeleteGame, this, placeholder::_1)));
}

View file

@ -24,7 +24,7 @@
// set game specific settings, etc.
// Uses GameInfoCache heavily to implement the functionality.
class GameScreen : public UIScreen {
class GameScreen : public DialogScreen {
public:
GameScreen(std::string gamePath) : gamePath_(gamePath) {}

View file

@ -41,7 +41,6 @@ namespace MainWindow {
}
#endif
namespace UI {
// Reads and writes value to determine the current selection.
@ -157,7 +156,7 @@ EventReturn PopupSliderChoiceFloat::HandleClick(EventParams &e) {
void PopupSliderChoiceFloat::Draw(UIContext &dc) {
Choice::Draw(dc);
char temp[4];
char temp[5];
sprintf(temp, "%2.2f", *value_);
dc.Draw()->DrawText(dc.theme->uiFont, temp, bounds_.x2() - 8, bounds_.centerY(), 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_VCENTER);
}
@ -180,6 +179,7 @@ void GameSettingsScreen::CreateViews() {
I18NCategory *c = GetI18NCategory("Controls");
I18NCategory *a = GetI18NCategory("Audio");
I18NCategory *s = GetI18NCategory("System");
I18NCategory *ms = GetI18NCategory("MainSettings");
Margins actionMenuMargins(0, 0, 15, 0);
@ -189,9 +189,9 @@ void GameSettingsScreen::CreateViews() {
root_->Add(leftColumn);
leftColumn->Add(new Spacer(new LinearLayoutParams(1.0)));
leftColumn->Add(new Choice(g->T("Back"), "", false, new AnchorLayoutParams(205, WRAP_CONTENT, 30, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
leftColumn->Add(new Choice(g->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new LinearLayoutParams(910, FILL_PARENT, actionMenuMargins));
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new LinearLayoutParams(800, FILL_PARENT, actionMenuMargins));
root_->Add(tabHolder);
@ -201,7 +201,7 @@ void GameSettingsScreen::CreateViews() {
ViewGroup *graphicsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
ViewGroup *graphicsSettings = new LinearLayout(ORIENT_VERTICAL);
graphicsSettingsScroll->Add(graphicsSettings);
tabHolder->AddTab("Graphics", graphicsSettingsScroll);
tabHolder->AddTab(ms->T("Graphics"), graphicsSettingsScroll);
graphicsSettings->Add(new ItemHeader(gs->T("Rendering Mode")));
#ifndef USING_GLES2
@ -251,7 +251,7 @@ void GameSettingsScreen::CreateViews() {
ViewGroup *audioSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
ViewGroup *audioSettings = new LinearLayout(ORIENT_VERTICAL);
audioSettingsScroll->Add(audioSettings);
tabHolder->AddTab("Audio", audioSettingsScroll);
tabHolder->AddTab(ms->T("Audio"), audioSettingsScroll);
std::string atracString;
atracString.assign(Atrac3plus_Decoder::IsInstalled() ? "Redownload Atrac3+ plugin" : "Download Atrac3+ plugin");
@ -266,19 +266,21 @@ void GameSettingsScreen::CreateViews() {
ViewGroup *controlsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
ViewGroup *controlsSettings = new LinearLayout(ORIENT_VERTICAL);
controlsSettingsScroll->Add(controlsSettings);
tabHolder->AddTab("Controls", controlsSettingsScroll);
tabHolder->AddTab(ms->T("Controls"), controlsSettingsScroll);
controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, c->T("OnScreen", "On-Screen Touch Controls")));
controlsSettings->Add(new CheckBox(&g_Config.bShowAnalogStick, c->T("Show Left Analog Stick")));
controlsSettings->Add(new CheckBox(&g_Config.bAccelerometerToAnalogHoriz, c->T("Tilt", "Tilt to Analog (horizontal)")));
controlsSettings->Add(new Choice(gs->T("Control Mapping")))->OnClick.Handle(this, &GameSettingsScreen::OnControlMapping);
controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 15, 65, c->T("Button Opacity"), screenManager()));
controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 0, 85, c->T("Button Opacity"), screenManager()));
controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fButtonScale, 1.15, 2.05, c->T("Button Scaling"), screenManager()));
// System
ViewGroup *systemSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
ViewGroup *systemSettings = new LinearLayout(ORIENT_VERTICAL);
systemSettingsScroll->Add(systemSettings);
tabHolder->AddTab("System", systemSettingsScroll);
tabHolder->AddTab(ms->T("System"), systemSettingsScroll);
systemSettings->Add(new CheckBox(&g_Config.bJit, s->T("Dynarec", "Dynarec (JIT)")));
systemSettings->Add(new CheckBox(&g_Config.bSeparateCPUThread, s->T("Multithreaded (experimental)")));
systemSettings->Add(new CheckBox(&g_Config.bFastMemory, s->T("Fast Memory", "Fast Memory (Unstable)")));
systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, gs->T("Unlock CPU Clock"), screenManager()));
systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, s->T("Day Light Saving")));
@ -319,6 +321,7 @@ void GameSettingsScreen::DrawBackground(UIContext &dc) {
dc.RebindTexture();
}*/
}
void GameSettingsScreen::update(InputState &input) {
UIScreen::update(input);
g_Config.iForceMaxEmulatedFPS = cap60FPS_ ? 60 : 0;
@ -334,11 +337,12 @@ void GlobalSettingsScreen::CreateViews() {
I18NCategory *gs = GetI18NCategory("Graphics");
LinearLayout *list = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
list->Add(new ItemHeader("General"));
list->Add(new ItemHeader(g->T("General")));
list->Add(new CheckBox(&g_Config.bNewUI, gs->T("Enable New UI")));
list->Add(new CheckBox(&enableReports_, gs->T("Enable Compatibility Server Reports")));
list->Add(new CheckBox(&g_Config.bEnableCheats, gs->T("Enable Cheats")));
list->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, gs->T("Screenshots as PNG")));
list->Add(new CheckBox(&g_Config.bDirectLoad, gs->T("Enable Direct Load")));
list->Add(new Choice(gs->T("System Language")))->OnClick.Handle(this, &GlobalSettingsScreen::OnLanguage);
list->Add(new Choice(gs->T("Developer Tools")))->OnClick.Handle(this, &GlobalSettingsScreen::OnDeveloperTools);
list->Add(new Choice(g->T("Back")))->OnClick.Handle(this, &GlobalSettingsScreen::OnBack);

View file

@ -22,7 +22,7 @@
// Per-game settings screen - enables you to configure graphic options, control options, etc
// per game.
class GameSettingsScreen : public UIScreenWithBackground {
class GameSettingsScreen : public UIDialogScreenWithBackground {
public:
GameSettingsScreen(std::string gamePath, std::string gameID = "") : gamePath_(gamePath), gameID_(gameID) {}
@ -48,7 +48,7 @@ private:
};
// TODO: Move to its own file.
class GlobalSettingsScreen : public UIScreenWithBackground {
class GlobalSettingsScreen : public UIDialogScreenWithBackground {
public:
GlobalSettingsScreen() {}
@ -66,7 +66,7 @@ private:
bool enableReports_;
};
class DeveloperToolsScreen : public UIScreenWithBackground {
class DeveloperToolsScreen : public UIDialogScreenWithBackground {
public:
DeveloperToolsScreen() {}

View file

@ -58,7 +58,7 @@ void MultiTouchButton::Draw(UIContext &dc) {
float scale = scale_;
if (IsDown()) {
scale *= 2.0f;
opacity = 100.0f;
opacity *= 1.15f;
}
uint32_t colorBg = colorAlpha(0xc0b080, opacity);
uint32_t color = colorAlpha(0xFFFFFF, opacity);

View file

@ -35,6 +35,7 @@
#include "UI/ui_atlas.h"
#include "Core/Config.h"
#include "GPU/GPUInterface.h"
#include "i18n/i18n.h"
#ifdef _WIN32
namespace MainWindow {
@ -62,6 +63,8 @@ public:
virtual void Update(const InputState &input_state) {
if (down_)
holdFrameCount_++;
else
holdFrameCount_ = 0;
// Hold button for 1.5 seconds to launch the game directly
if (holdFrameCount_ > 90) {
UI::EventParams e;
@ -114,14 +117,16 @@ void GameButton::Draw(UIContext &dc) {
// Render button
int dropsize = 10;
if (texture) {
if (txOffset) {
dropsize = 3;
y += txOffset * 2;
}
if (HasFocus()) {
// dc.Draw()->DrawImage4Grid(I_DROP_SHADOW, x - dropsize, y, x+w + dropsize, y+h+dropsize*1.5, alphaMul(color, 0.5f), 1.0f);
// dc.Draw()->Flush();
} else {
if (txOffset) {
dropsize = 3;
y += txOffset * 2;
}
dc.Draw()->Flush();
dc.RebindTexture();
dc.Draw()->DrawImage4Grid(I_DROP_SHADOW, x - dropsize, y, x+w + dropsize, y+h+dropsize*1.5, alphaMul(shadowColor, 0.5f), 1.0f);
dc.Draw()->Flush();
}
@ -336,6 +341,8 @@ void MainScreen::CreateViews() {
// Scrolling action menu to the right.
using namespace UI;
I18NCategory *m = GetI18NCategory("MainMenu");
Margins actionMenuMargins(0, 100, 15, 0);
root_ = new LinearLayout(ORIENT_HORIZONTAL);
@ -355,9 +362,9 @@ void MainScreen::CreateViews() {
scrollAllGames->Add(tabAllGames);
scrollHomebrew->Add(tabHomebrew);
leftColumn->AddTab("Recent", scrollRecentGames);
leftColumn->AddTab("Games", scrollAllGames);
leftColumn->AddTab("Homebrew & Demos", scrollHomebrew);
leftColumn->AddTab(m->T("Recent"), scrollRecentGames);
leftColumn->AddTab(m->T("Games"), scrollAllGames);
leftColumn->AddTab(m->T("Homebrew & Demos"), scrollHomebrew);
tabRecentGames->OnChoice.Handle(this, &MainScreen::OnGameSelected);
tabAllGames->OnChoice.Handle(this, &MainScreen::OnGameSelected);
@ -382,12 +389,12 @@ void MainScreen::CreateViews() {
rightColumn->Add(rightColumnItems);
#ifdef _WIN32
rightColumnItems->Add(new Choice("Load..."))->OnClick.Handle(this, &MainScreen::OnLoadFile);
rightColumnItems->Add(new Choice(m->T("Load","Load...")))->OnClick.Handle(this, &MainScreen::OnLoadFile);
#endif
rightColumnItems->Add(new Choice("Settings"))->OnClick.Handle(this, &MainScreen::OnSettings);
rightColumnItems->Add(new Choice("Exit"))->OnClick.Handle(this, &MainScreen::OnExit);
rightColumnItems->Add(new Choice("Credits"))->OnClick.Handle(this, &MainScreen::OnCredits);
rightColumnItems->Add(new Choice("Support PPSSPP"))->OnClick.Handle(this, &MainScreen::OnSupport);
rightColumnItems->Add(new Choice(m->T("Settings")))->OnClick.Handle(this, &MainScreen::OnSettings);
rightColumnItems->Add(new Choice(m->T("Exit")))->OnClick.Handle(this, &MainScreen::OnExit);
rightColumnItems->Add(new Choice(m->T("Credits")))->OnClick.Handle(this, &MainScreen::OnCredits);
rightColumnItems->Add(new Choice(m->T("Support PPSSPP")))->OnClick.Handle(this, &MainScreen::OnSupport);
}
void MainScreen::sendMessage(const char *message, const char *value) {
@ -477,6 +484,8 @@ GamePauseScreen::~GamePauseScreen() {
void GamePauseScreen::CreateViews() {
using namespace UI;
Margins actionMenuMargins(0, 100, 15, 0);
I18NCategory *gs = GetI18NCategory("Graphics");
I18NCategory *i = GetI18NCategory("Pause");
root_ = new LinearLayout(ORIENT_HORIZONTAL);
@ -495,8 +504,8 @@ void GamePauseScreen::CreateViews() {
saveSlots_->AddChoice("4");
saveSlots_->SetSelection(g_Config.iCurrentStateSlot);
leftColumnItems->Add(new Choice("Save State"))->OnClick.Handle(this, &GamePauseScreen::OnSaveState);
leftColumnItems->Add(new Choice("Load State"))->OnClick.Handle(this, &GamePauseScreen::OnLoadState);
leftColumnItems->Add(new Choice(gs->T("Save State")))->OnClick.Handle(this, &GamePauseScreen::OnSaveState);
leftColumnItems->Add(new Choice(gs->T("Load State")))->OnClick.Handle(this, &GamePauseScreen::OnLoadState);
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
root_->Add(rightColumn);
@ -504,10 +513,10 @@ void GamePauseScreen::CreateViews() {
ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL);
rightColumn->Add(rightColumnItems);
rightColumnItems->Add(new Choice("Continue"))->OnClick.Handle(this, &GamePauseScreen::OnContinue);
rightColumnItems->Add(new Choice("Game Settings"))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings);
rightColumnItems->Add(new Choice("Main Settings"))->OnClick.Handle(this, &GamePauseScreen::OnMainSettings);
rightColumnItems->Add(new Choice("Exit to menu"))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu);
rightColumnItems->Add(new Choice(i->T("Continue")))->OnClick.Handle(this, &GamePauseScreen::OnContinue);
rightColumnItems->Add(new Choice(i->T("Game Settings")))->OnClick.Handle(this, &GamePauseScreen::OnGameSettings);
rightColumnItems->Add(new Choice(i->T("Main Settings")))->OnClick.Handle(this, &GamePauseScreen::OnMainSettings);
rightColumnItems->Add(new Choice(i->T("Exit to menu")))->OnClick.Handle(this, &GamePauseScreen::OnExitToMenu);
}
UI::EventReturn GamePauseScreen::OnMainSettings(UI::EventParams &e) {

View file

@ -253,6 +253,7 @@ void MenuScreen::render() {
VLinear vlinear(dp_xres + xoff, 100, 20);
I18NCategory *m = GetI18NCategory("MainMenu");
I18NCategory *p = GetI18NCategory("Plugin");
if (UIButton(GEN_ID, vlinear, w, 0, m->T("Load", "Load..."), ALIGN_RIGHT)) {
#if defined(USING_QT_UI) && !defined(MEEGO_EDITION_HARMATTAN)
@ -312,7 +313,7 @@ void MenuScreen::render() {
}
if (showAtracShortcut_) {
if (UIButton(GEN_ID, Pos(10,dp_yres - 10), 500, 50, "Download audio plugin", ALIGN_BOTTOMLEFT)) {
if (UIButton(GEN_ID, Pos(10, dp_yres - 10), 500, 50, p->T("Download audio plugin"), ALIGN_BOTTOMLEFT)) {
screenManager()->push(new PluginScreen());
}
}
@ -844,6 +845,7 @@ void AudioScreen::render() {
I18NCategory *g = GetI18NCategory("General");
I18NCategory *a = GetI18NCategory("Audio");
I18NCategory *p = GetI18NCategory("Plugin");
ui_draw2d.SetFontScale(1.5f, 1.5f);
ui_draw2d.DrawTextShadow(UBUNTU24, a->T("Audio Settings"), dp_xres / 2, 10, 0xFFFFFFFF, ALIGN_HCENTER);
@ -866,7 +868,7 @@ void AudioScreen::render() {
// Show the download button even if not installed - might want to upgrade.
VLinear vlinear(30, 400, 20);
std::string atracString;
atracString.assign(Atrac3plus_Decoder::IsInstalled() ? "Redownload Atrac3+ plugin" : "Download Atrac3+ plugin");
atracString.assign(Atrac3plus_Decoder::IsInstalled() ? p->T("Redownload Atrac3+ plugin") : p->T("Download Atrac3+ plugin"));
if (UIButton(GEN_ID, vlinear, 400, 0, a->T(atracString.c_str()), ALIGN_LEFT)) {
screenManager()->push(new PluginScreen());
}
@ -1436,6 +1438,8 @@ void SystemScreen::render() {
if (g_Config.bJit)
UICheckBox(GEN_ID, x, y += stride, s->T("Fast Memory", "Fast Memory (unstable)"), ALIGN_TOPLEFT, &g_Config.bFastMemory);
UICheckBox(GEN_ID, x, y += stride, s->T("Multithreaded (experimental)"), ALIGN_TOPLEFT, &g_Config.bSeparateCPUThread);
bool LockCPUSpeed = g_Config.iLockedCPUSpeed != 0;
UICheckBox(GEN_ID, x, y += stride, s->T("Unlock CPU Clock"), ALIGN_TOPLEFT, &LockCPUSpeed);
if(LockCPUSpeed) {
@ -1542,7 +1546,7 @@ void SystemScreen::render() {
char nickname[512];
memset(nickname, 0, sizeof(nickname));
sprintf(nickname, "%s %s", s->T("System Nickname: "), g_Config.sNickName.c_str());
sprintf(nickname, "%s %s", s->T("System Nickname:"), g_Config.sNickName.c_str());
ui_draw2d.DrawTextShadow(UBUNTU24, nickname, x, y += stride, 0xFFFFFFFF, ALIGN_LEFT);
HLinear hlinearNick(x + 400, y, 10);
@ -1804,7 +1808,7 @@ void KeyMappingScreen::render() {
char temp[256];
sprintf(temp, "%s (%i/%i)", controllerMaps[currentMap_].name.c_str(), currentMap_ + 1, (int)controllerMaps.size());
UIText(0, Pos(10, dp_yres-170), temp, 0xFFFFFFFF, 1.0f, ALIGN_BOTTOMLEFT);
UICheckBox(GEN_ID,10, dp_yres - 80, "Mapping Active", ALIGN_BOTTOMLEFT, &controllerMaps[currentMap_].active);
UICheckBox(GEN_ID,10, dp_yres - 80, keyI18N->T("Mapping Active"), ALIGN_BOTTOMLEFT, &controllerMaps[currentMap_].active);
UIEnd();
}
@ -1837,7 +1841,7 @@ void KeyMappingNewKeyDialog::render() {
int left = 10;
int stride = 70;
KeyScale(1.6f);
KeyText(left, top, keyI18N->T("Map a new key for button: "));
KeyText(left, top, keyI18N->T("Map a new key for button:"));
KeyText(left, top += stride, (KeyMap::GetPspButtonName(this->pspBtn)).c_str());
KeyScale(1.3f);
KeyText(left, top += stride, keyI18N->T("Current key"));

View file

@ -34,6 +34,11 @@ void UIScreenWithBackground::DrawBackground(UIContext &dc) {
dc.Flush();
}
void UIDialogScreenWithBackground::DrawBackground(UIContext &dc) {
::DrawBackground(1.0f);
dc.Flush();
}
void PromptScreen::CreateViews() {
// Information in the top left.
// Back button to the bottom left.
@ -108,6 +113,7 @@ NewLanguageScreen::NewLanguageScreen() : ListPopupScreen("Language") {
VFSGetFileListing("lang", &langs_, "ini");
#endif
std::vector<std::string> listing;
int selected = -1;
for (size_t i = 0; i < langs_.size(); i++) {
// Skip README
if (langs_[i].name.find("README") != std::string::npos) {
@ -129,10 +135,12 @@ NewLanguageScreen::NewLanguageScreen() : ListPopupScreen("Language") {
buttonTitle = langValuesMapping[code].first;
}
}
if (g_Config.languageIni == code)
selected = i;
listing.push_back(buttonTitle);
}
adaptor_ = UI::StringVectorListAdaptor(listing, 0);
adaptor_ = UI::StringVectorListAdaptor(listing, selected);
}
void NewLanguageScreen::OnCompleted() {

View file

@ -34,8 +34,14 @@ protected:
virtual void DrawBackground(UIContext &dc);
};
class UIDialogScreenWithBackground : public DialogScreen {
public:
UIDialogScreenWithBackground() : DialogScreen() {}
protected:
virtual void DrawBackground(UIContext &dc);
};
class PromptScreen : public UIScreenWithBackground {
class PromptScreen : public UIDialogScreenWithBackground {
public:
PromptScreen(std::string message, std::string yesButtonText, std::string noButtonText)
: message_(message), yesButtonText_(yesButtonText), noButtonText_(noButtonText) {

View file

@ -48,7 +48,7 @@ void PluginScreen::CreateViews() {
Margins textMargins(20,17);
Margins buttonMargins(10,10);
root_->Add(new TextView(UBUNTU24, "Atrac3+ Audio Support", ALIGN_HCENTER, 1.5f, new LinearLayoutParams(textMargins)));
root_->Add(new TextView(UBUNTU24, p->T("Atrac3+ Audio Support"), ALIGN_HCENTER, 1.5f, new LinearLayoutParams(textMargins)));
ViewGroup *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
LinearLayout *scrollContents = new LinearLayout(ORIENT_VERTICAL);

View file

@ -9,7 +9,7 @@ namespace DSound
#define MAXWAIT 20 //ms
CRITICAL_SECTION soundCriticalSection;
HANDLE soundSyncEvent;
HANDLE soundSyncEvent = NULL;
HANDLE hThread;
StreamCallback callback;
@ -183,7 +183,8 @@ namespace DSound
void DSound_UpdateSound()
{
SetEvent(soundSyncEvent);
if (soundSyncEvent != NULL)
SetEvent(soundSyncEvent);
}
@ -201,6 +202,7 @@ namespace DSound
ds->Release();
CloseHandle(soundSyncEvent);
soundSyncEvent = NULL;
}

View file

@ -847,6 +847,7 @@ void CtrlDisAsmView::updateStatusBarText()
sprintf(text,"[%08X] = %04X",info.dataAddress,Memory::Read_U16(info.dataAddress));
break;
case 4:
// TODO: Could also be a float...
{
u32 data = Memory::Read_U32(info.dataAddress);
const char* addressSymbol = debugger->findSymbolForAddress(data);
@ -858,6 +859,9 @@ void CtrlDisAsmView::updateStatusBarText()
}
break;
}
case 16:
// TODO: vector
break;
}
}
}

View file

@ -138,10 +138,17 @@ CDisasm::CDisasm(HINSTANCE _hInstance, HWND _hParent, DebugInterface *_cpu) : Di
threadList->setDialogItem(GetDlgItem(m_hDlg,IDC_THREADLIST));
threadList->reloadThreads();
stackTraceView = new CtrlStackTraceView();
stackTraceView->setCpu(cpu);
stackTraceView->setDisasm(ptr);
stackTraceView->setDialogItem(GetDlgItem(m_hDlg,IDC_STACKFRAMES));
stackTraceView->loadStackTrace();
// init memory/breakpoint "tab"
ShowWindow(GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST), SW_HIDE);
ShowWindow(GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), SW_NORMAL);
ShowWindow(GetDlgItem(m_hDlg, IDC_THREADLIST), SW_HIDE);
ShowWindow(GetDlgItem(m_hDlg, IDC_STACKFRAMES), SW_HIDE);
// init status bar
statusBarWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", m_hDlg, IDC_DISASMSTATUSBAR);
@ -206,6 +213,9 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
case IDC_THREADLIST:
threadList->handleNotify(lParam);
break;
case IDC_STACKFRAMES:
stackTraceView->handleNotify(lParam);
break;
}
break;
case WM_COMMAND:
@ -283,6 +293,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
CtrlMemView::getFrom(GetDlgItem(m_hDlg,IDC_DEBUGMEMVIEW))->redraw();
threadList->reloadThreads();
stackTraceView->loadStackTrace();
updateThreadLabel(false);
}
break;
@ -504,23 +515,34 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam)
HWND bp = GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST);
HWND mem = GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW);
HWND threads = GetDlgItem(m_hDlg, IDC_THREADLIST);
HWND stackFrames = GetDlgItem(m_hDlg, IDC_STACKFRAMES);
if (IsWindowVisible(bp))
{
ShowWindow(bp,SW_HIDE);
ShowWindow(mem,SW_HIDE);
ShowWindow(bp,SW_HIDE);
ShowWindow(threads,SW_NORMAL);
ShowWindow(stackFrames,SW_HIDE);
SetFocus(threads);
} else if (IsWindowVisible(threads))
{
ShowWindow(mem,SW_HIDE);
ShowWindow(bp,SW_HIDE);
ShowWindow(mem,SW_NORMAL);
ShowWindow(threads,SW_HIDE);
ShowWindow(stackFrames,SW_NORMAL);
SetFocus(stackFrames);
} else if (IsWindowVisible(stackFrames))
{
ShowWindow(mem,SW_NORMAL);
ShowWindow(bp,SW_HIDE);
ShowWindow(threads,SW_HIDE);
ShowWindow(stackFrames,SW_HIDE);
SetFocus(mem);
} else {
ShowWindow(bp,SW_NORMAL);
ShowWindow(mem,SW_HIDE);
ShowWindow(bp,SW_NORMAL);
ShowWindow(threads,SW_HIDE);
ShowWindow(stackFrames,SW_HIDE);
SetFocus(bp);
}
}
@ -590,6 +612,7 @@ void CDisasm::UpdateSize(WORD width, WORD height)
HWND breakpointList = GetDlgItem(m_hDlg, IDC_BREAKPOINTLIST);
HWND memView = GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW);
HWND threads = GetDlgItem(m_hDlg, IDC_THREADLIST);
HWND stackFrame = GetDlgItem(m_hDlg,IDC_STACKFRAMES);
if (g_Config.bDisplayStatusBar)
{
@ -617,6 +640,7 @@ void CDisasm::UpdateSize(WORD width, WORD height)
MoveWindow(breakpointList,8,breakpointTop,width-16,breakpointHeight,TRUE);
MoveWindow(memView,8,breakpointTop,width-16,breakpointHeight,TRUE);
MoveWindow(threads,8,breakpointTop,width-16,breakpointHeight,TRUE);
MoveWindow(stackFrame,8,breakpointTop,width-16,breakpointHeight,TRUE);
GetWindowRect(GetDlgItem(m_hDlg, IDC_REGLIST),&regRect);
GetWindowRect(GetDlgItem(m_hDlg, IDC_DISASMVIEW),&disRect);
@ -646,6 +670,7 @@ void CDisasm::SetDebugMode(bool _bDebug)
CBreakPoints::ClearTemporaryBreakPoints();
breakpointList->update();
threadList->reloadThreads();
stackTraceView->loadStackTrace();
updateThreadLabel(false);
EnableWindow( GetDlgItem(hDlg, IDC_GO), TRUE);

View file

@ -30,6 +30,7 @@ private:
HWND statusBarWnd;
CtrlBreakpointList* breakpointList;
CtrlThreadList* threadList;
CtrlStackTraceView* stackTraceView;
std::vector<BreakPoint> displayedBreakPoints_;
std::vector<MemCheck> displayedMemChecks_;

View file

@ -7,6 +7,7 @@
#include "Windows/resource.h"
#include "Windows/main.h"
#include "BreakpointWindow.h"
#include "../../Core/HLE/sceKernelThread.h"
typedef struct
{
@ -16,6 +17,7 @@ typedef struct
enum { TL_NAME, TL_PROGRAMCOUNTER, TL_ENTRYPOINT, TL_PRIORITY, TL_STATE, TL_WAITTYPE, TL_COLUMNCOUNT };
enum { BPL_TYPE, BPL_OFFSET, BPL_SIZELABEL, BPL_OPCODE, BPL_CONDITION, BPL_HITS, BPL_ENABLED, BPL_COLUMNCOUNT };
enum { SF_ENTRY, SF_ENTRYNAME, SF_CURPC, SF_CUROPCODE, SF_CURSP, SF_FRAMESIZE, SF_COLUMNCOUNT };
ListViewColumn threadColumns[TL_COLUMNCOUNT] = {
{ "Name", 0.20f },
@ -36,6 +38,15 @@ ListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = {
{ "Enabled", 0.08f }
};
ListViewColumn stackTraceColumns[SF_COLUMNCOUNT] = {
{ "Entry", 0.12f },
{ "Name", 0.24f },
{ "PC", 0.12f },
{ "Opcode", 0.28f },
{ "SP", 0.12f },
{ "Frame Size", 0.12f }
};
const int POPUP_SUBMENU_ID_BREAKPOINTLIST = 5;
const int POPUP_SUBMENU_ID_THREADLIST = 6;
const int POPUP_SUBMENU_ID_NEWBREAKPOINT = 7;
@ -710,3 +721,163 @@ void CtrlBreakpointList::showBreakpointMenu(int itemIndex, const POINT &pt)
}
}
}
//
// CtrlStackTraceView
//
void CtrlStackTraceView::setDialogItem(HWND hwnd)
{
wnd = hwnd;
SetWindowLongPtr(wnd,GWLP_USERDATA,(LONG_PTR)this);
oldProc = (WNDPROC) SetWindowLongPtr(wnd,GWLP_WNDPROC,(LONG_PTR)wndProc);
SendMessage(wnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.iSubItem = 0;
lvc.fmt = LVCFMT_LEFT;
RECT rect;
GetWindowRect(wnd,&rect);
int totalListSize = (rect.right-rect.left-20);
for (int i = 0; i < SF_COLUMNCOUNT; i++)
{
lvc.cx = stackTraceColumns[i].size * totalListSize;
lvc.pszText = stackTraceColumns[i].name;
ListView_InsertColumn(wnd, i, &lvc);
}
}
LRESULT CALLBACK CtrlStackTraceView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CtrlStackTraceView* sv = (CtrlStackTraceView*) GetWindowLongPtr(hwnd,GWLP_USERDATA);
switch (msg)
{
case WM_SIZE:
{
int width = LOWORD(lParam);
RECT rect;
GetWindowRect(hwnd,&rect);
int totalListSize = (rect.right-rect.left-20);
for (int i = 0; i < SF_COLUMNCOUNT; i++)
{
ListView_SetColumnWidth(hwnd,i,stackTraceColumns[i].size * totalListSize);
}
}
break;
case WM_KEYDOWN:
if (wParam == VK_TAB)
{
SendMessage(GetParent(hwnd),WM_DEB_TABPRESSED,0,0);
return 0;
}
break;
case WM_GETDLGCODE:
if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)
{
if (wParam == VK_TAB) return DLGC_WANTMESSAGE;
}
break;
}
return (LRESULT)CallWindowProc((WNDPROC)sv->oldProc,hwnd,msg,wParam,lParam);
}
void CtrlStackTraceView::handleNotify(LPARAM lParam)
{
LPNMHDR mhdr = (LPNMHDR) lParam;
if (mhdr->code == NM_DBLCLK)
{
LPNMITEMACTIVATE item = (LPNMITEMACTIVATE) lParam;
SendMessage(GetParent(wnd),WM_DEB_GOTOWPARAM,frames[item->iItem].pc,0);
return;
}
if (mhdr->code == LVN_GETDISPINFO)
{
NMLVDISPINFO* dispInfo = (NMLVDISPINFO*)lParam;
int index = dispInfo->item.iItem;
stringBuffer[0] = 0;
switch (dispInfo->item.iSubItem)
{
case SF_ENTRY:
sprintf(stringBuffer,"%08X",frames[index].entry);
break;
case SF_ENTRYNAME:
{
const char* sym = cpu->findSymbolForAddress(frames[index].entry);
if (sym != NULL)
{
strcpy(stringBuffer,sym);
} else {
strcpy(stringBuffer,"-");
}
}
break;
case SF_CURPC:
sprintf(stringBuffer,"%08X",frames[index].pc);
break;
case SF_CUROPCODE:
disasm->getOpcodeText(frames[index].pc,stringBuffer);
break;
case SF_CURSP:
sprintf(stringBuffer,"%08X",frames[index].sp);
break;
case SF_FRAMESIZE:
sprintf(stringBuffer,"%08X",frames[index].stackSize);
break;
}
if (stringBuffer[0] == 0) strcat(stringBuffer,"Invalid");
dispInfo->item.pszText = stringBuffer;
}
}
void CtrlStackTraceView::loadStackTrace()
{
auto threads = GetThreadsInfo();
u32 entry, stackTop;
for (size_t i = 0; i < threads.size(); i++)
{
if (threads[i].isCurrent)
{
entry = threads[i].entrypoint;
stackTop = threads[i].initialStack;
break;
}
}
frames = MIPSStackWalk::Walk(cpu->GetPC(),cpu->GetRegValue(0,31),cpu->GetRegValue(0,29),entry,stackTop);
int items = ListView_GetItemCount(wnd);
while (items < (int)frames.size())
{
LVITEM lvI;
lvI.pszText = LPSTR_TEXTCALLBACK; // Sends an LVN_GETDISPINFO message.
lvI.mask = LVIF_TEXT | LVIF_IMAGE |LVIF_STATE;
lvI.stateMask = 0;
lvI.iSubItem = 0;
lvI.state = 0;
lvI.iItem = items;
lvI.iImage = items;
ListView_InsertItem(wnd, &lvI);
items++;
}
while (items > (int)frames.size())
{
ListView_DeleteItem(wnd,--items);
}
InvalidateRect(wnd,NULL,true);
UpdateWindow(wnd);
}

View file

@ -3,6 +3,7 @@
#include "../../Core/Debugger/DebugInterface.h"
#include "../../Core/HLE/sceKernelThread.h"
#include "../../Core/Debugger/Breakpoints.h"
#include "../../Core/MIPS/MIPSStackWalk.h"
class CtrlThreadList
{
@ -53,4 +54,28 @@ public:
void handleNotify(LPARAM lParam);
void showMenu(int itemIndex, const POINT &pt);
static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
class CtrlStackTraceView
{
HWND wnd;
WNDPROC oldProc;
std::vector<MIPSStackWalk::StackFrame> frames;
DebugInterface* cpu;
CtrlDisAsmView* disasm;
char stringBuffer[256];
public:
void setCpu(DebugInterface* cpu)
{
this->cpu = cpu;
};
void setDisasm(CtrlDisAsmView* disasm)
{
this->disasm = disasm;
};
void setDialogItem(HWND hwnd);
void loadStackTrace();
void handleNotify(LPARAM lParam);
static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};

Some files were not shown because too many files have changed in this diff Show more