mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-09 12:36:23 +02:00
Merge pull request #7227 from unknownbrackets/io-minor
Add an option to prevent cpu stall on slow IO
This commit is contained in:
commit
442fe9cccc
15 files changed
+302
-67
No files matched your search
@@ -337,6 +337,7 @@ static ConfigSetting cpuSettings[] = {
|
||||
ConfigSetting("AtomicAudioLocks", &g_Config.bAtomicAudioLocks, false, true, true),
|
||||
|
||||
ReportedConfigSetting("SeparateIOThread", &g_Config.bSeparateIOThread, true, true, true),
|
||||
ReportedConfigSetting("IOTimingMethod", &g_Config.iIOTimingMethod, IOTIMING_FAST, true, true),
|
||||
ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, true, true),
|
||||
ReportedConfigSetting("FuncReplacements", &g_Config.bFuncReplacements, true, true, true),
|
||||
ReportedConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0, true, true),
|
||||
|
||||
@@ -50,6 +50,13 @@ enum {
|
||||
GPU_BACKEND_DIRECT3D9 = 1,
|
||||
};
|
||||
|
||||
// For iIOTimingMethod.
|
||||
enum IOTimingMethods {
|
||||
IOTIMING_FAST = 0,
|
||||
IOTIMING_HOST = 1,
|
||||
IOTIMING_REALISTIC = 2,
|
||||
};
|
||||
|
||||
namespace http {
|
||||
class Download;
|
||||
class Downloader;
|
||||
@@ -109,6 +116,7 @@ public:
|
||||
|
||||
// Definitely cannot be changed while game is running.
|
||||
bool bSeparateCPUThread;
|
||||
int iIOTimingMethod;
|
||||
bool bSeparateIOThread;
|
||||
bool bAtomicAudioLocks;
|
||||
int iLockedCPUSpeed;
|
||||
|
||||
@@ -600,6 +600,11 @@ int DirectoryFileSystem::DevType(u32 handle) {
|
||||
}
|
||||
|
||||
size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
int ignored;
|
||||
return ReadFile(handle, pointer, size, ignored);
|
||||
}
|
||||
|
||||
size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
|
||||
EntryMap::iterator iter = entries.find(handle);
|
||||
if (iter != entries.end())
|
||||
{
|
||||
@@ -613,6 +618,11 @@ size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
}
|
||||
|
||||
size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
|
||||
int ignored;
|
||||
return WriteFile(handle, pointer, size, ignored);
|
||||
}
|
||||
|
||||
size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {
|
||||
EntryMap::iterator iter = entries.find(handle);
|
||||
if (iter != entries.end())
|
||||
{
|
||||
@@ -971,6 +981,11 @@ int VFSFileSystem::DevType(u32 handle) {
|
||||
}
|
||||
|
||||
size_t VFSFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
int ignored;
|
||||
return ReadFile(handle, pointer, size, ignored);
|
||||
}
|
||||
|
||||
size_t VFSFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
|
||||
DEBUG_LOG(FILESYS,"VFSFileSystem::ReadFile %08x %p %i", handle, pointer, (u32)size);
|
||||
EntryMap::iterator iter = entries.find(handle);
|
||||
if (iter != entries.end())
|
||||
@@ -986,6 +1001,11 @@ size_t VFSFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
}
|
||||
|
||||
size_t VFSFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
|
||||
int ignored;
|
||||
return WriteFile(handle, pointer, size, ignored);
|
||||
}
|
||||
|
||||
size_t VFSFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {
|
||||
// NOT SUPPORTED - READ ONLY
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ public:
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) override;
|
||||
void CloseFile(u32 handle) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
||||
PSPFileInfo GetFileInfo(std::string filename) override;
|
||||
bool OwnsHandle(u32 handle) override;
|
||||
@@ -139,7 +141,9 @@ public:
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) override;
|
||||
void CloseFile(u32 handle) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
||||
PSPFileInfo GetFileInfo(std::string filename) override;
|
||||
bool OwnsHandle(u32 handle) override;
|
||||
|
||||
@@ -111,7 +111,9 @@ public:
|
||||
virtual u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) = 0;
|
||||
virtual void CloseFile(u32 handle) = 0;
|
||||
virtual size_t ReadFile(u32 handle, u8 *pointer, s64 size) = 0;
|
||||
virtual size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) = 0;
|
||||
virtual size_t WriteFile(u32 handle, const u8 *pointer, s64 size) = 0;
|
||||
virtual size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) = 0;
|
||||
virtual size_t SeekFile(u32 handle, s32 position, FileMove type) = 0;
|
||||
virtual PSPFileInfo GetFileInfo(std::string filename) = 0;
|
||||
virtual bool OwnsHandle(u32 handle) = 0;
|
||||
@@ -135,7 +137,9 @@ public:
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) {return 0;}
|
||||
void CloseFile(u32 handle) {}
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) {return 0;}
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {return 0;}
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) {return 0;}
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) {return 0;}
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) {return 0;}
|
||||
PSPFileInfo GetFileInfo(std::string filename) {PSPFileInfo f; return f;}
|
||||
bool OwnsHandle(u32 handle) {return false;}
|
||||
|
||||
@@ -227,6 +227,7 @@ void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root,
|
||||
{
|
||||
u8 theSector[2048];
|
||||
blockDevice->ReadBlock(secnum, theSector);
|
||||
lastReadBlock_ = secnum;
|
||||
|
||||
for (int offset = 0; offset < 2048; )
|
||||
{
|
||||
@@ -519,6 +520,12 @@ int ISOFileSystem::DevType(u32 handle)
|
||||
}
|
||||
|
||||
size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
||||
{
|
||||
int ignored;
|
||||
return ReadFile(handle, pointer, size, ignored);
|
||||
}
|
||||
|
||||
size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec)
|
||||
{
|
||||
EntryMap::iterator iter = entries.find(handle);
|
||||
if (iter != entries.end())
|
||||
@@ -529,7 +536,12 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
||||
{
|
||||
// Whole sectors! Shortcut to this simple code.
|
||||
blockDevice->ReadBlocks(e.seekPos, (int)size, pointer);
|
||||
if (abs((int)lastReadBlock_ - (int)e.seekPos) > 100) {
|
||||
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
|
||||
usec = 100000;
|
||||
}
|
||||
e.seekPos += (int)size;
|
||||
lastReadBlock_ = e.seekPos;
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
@@ -588,6 +600,11 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
||||
}
|
||||
|
||||
size_t totalBytes = pointer - start;
|
||||
if (abs((int)lastReadBlock_ - (int)secNum) > 100) {
|
||||
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
|
||||
usec = 100000;
|
||||
}
|
||||
lastReadBlock_ = secNum;
|
||||
e.seekPos += (unsigned int)totalBytes;
|
||||
return (size_t)totalBytes;
|
||||
}
|
||||
@@ -599,7 +616,13 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
|
||||
}
|
||||
}
|
||||
|
||||
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
|
||||
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
|
||||
{
|
||||
ERROR_LOG(FILESYS, "Hey, what are you doing? You can't write to an ISO!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ISOFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)
|
||||
{
|
||||
ERROR_LOG(FILESYS, "Hey, what are you doing? You can't write to an ISO!");
|
||||
return 0;
|
||||
@@ -738,7 +761,7 @@ ISOFileSystem::TreeEntry::~TreeEntry() {
|
||||
|
||||
void ISOFileSystem::DoState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("ISOFileSystem", 1);
|
||||
auto s = p.Section("ISOFileSystem", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -794,4 +817,10 @@ void ISOFileSystem::DoState(PointerWrap &p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s >= 2) {
|
||||
p.Do(lastReadBlock_);
|
||||
} else {
|
||||
lastReadBlock_ = 0;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename = NULL) override;
|
||||
void CloseFile(u32 handle) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
||||
PSPFileInfo GetFileInfo(std::string filename) override;
|
||||
bool OwnsHandle(u32 handle) override;
|
||||
@@ -46,6 +47,8 @@ public:
|
||||
u64 FreeSpace(const std::string &path) override { return 0; }
|
||||
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
||||
|
||||
bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;}
|
||||
bool MkDir(const std::string &dirname) override {return false;}
|
||||
bool RmDir(const std::string &dirname) override { return false; }
|
||||
@@ -83,6 +86,7 @@ private:
|
||||
IHandleAllocator *hAlloc;
|
||||
TreeEntry *treeroot;
|
||||
BlockDevice *blockDevice;
|
||||
u32 lastReadBlock_;
|
||||
|
||||
TreeEntry entireISO;
|
||||
|
||||
@@ -116,6 +120,9 @@ public:
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override {
|
||||
return isoFileSystem_->ReadFile(handle, pointer, size);
|
||||
}
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override {
|
||||
return isoFileSystem_->ReadFile(handle, pointer, size, usec);
|
||||
}
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) override {
|
||||
return isoFileSystem_->SeekFile(handle, position, type);
|
||||
}
|
||||
@@ -137,6 +144,9 @@ public:
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override {
|
||||
return isoFileSystem_->WriteFile(handle, pointer, size);
|
||||
}
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override {
|
||||
return isoFileSystem_->WriteFile(handle, pointer, size, usec);
|
||||
}
|
||||
bool GetHostPath(const std::string &inpath, std::string &outpath) { return false; }
|
||||
bool MkDir(const std::string &dirname) override { return false; }
|
||||
bool RmDir(const std::string &dirname) override { return false; }
|
||||
@@ -145,4 +155,4 @@ public:
|
||||
|
||||
private:
|
||||
ISOFileSystem *isoFileSystem_;
|
||||
};
|
||||
};
|
||||
@@ -555,7 +555,7 @@ 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);
|
||||
return sys->ReadFile(handle, pointer, size);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@@ -565,7 +565,27 @@ 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);
|
||||
return sys->WriteFile(handle, pointer, size);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
IFileSystem *sys = GetHandleOwner(handle);
|
||||
if (sys)
|
||||
return sys->ReadFile(handle, pointer, size, usec);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)
|
||||
{
|
||||
lock_guard guard(lock);
|
||||
IFileSystem *sys = GetHandleOwner(handle);
|
||||
if (sys)
|
||||
return sys->WriteFile(handle, pointer, size, usec);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ public:
|
||||
u32 OpenWithError(int &error, std::string filename, FileAccess access, const char *devicename = NULL);
|
||||
void CloseFile(u32 handle) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
||||
PSPFileInfo GetFileInfo(std::string filename) override;
|
||||
bool OwnsHandle(u32 handle) override { return false; }
|
||||
|
||||
@@ -146,7 +146,7 @@ void VirtualDiscFileSystem::LoadFileListIndex() {
|
||||
|
||||
void VirtualDiscFileSystem::DoState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("VirtualDiscFileSystem", 1);
|
||||
auto s = p.Section("VirtualDiscFileSystem", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -217,6 +217,12 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p)
|
||||
}
|
||||
}
|
||||
|
||||
if (s >= 2) {
|
||||
p.Do(lastReadBlock_);
|
||||
} else {
|
||||
lastReadBlock_ = 0;
|
||||
}
|
||||
|
||||
// We don't savestate handlers (loaded on fs load), but if they change, it may not load properly.
|
||||
}
|
||||
|
||||
@@ -422,6 +428,11 @@ size_t VirtualDiscFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
|
||||
}
|
||||
|
||||
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
int ignored;
|
||||
return ReadFile(handle, pointer, size, ignored);
|
||||
}
|
||||
|
||||
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
|
||||
EntryMap::iterator iter = entries.find(handle);
|
||||
if (iter != entries.end())
|
||||
{
|
||||
@@ -468,6 +479,12 @@ size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
|
||||
temp.Close();
|
||||
|
||||
iter->second.curOffset += size;
|
||||
// TODO: This probably isn't enough...
|
||||
if (abs((int)lastReadBlock_ - (int)iter->second.curOffset) > 100) {
|
||||
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
|
||||
usec = 100000;
|
||||
}
|
||||
lastReadBlock_ = iter->second.curOffset;
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -701,6 +718,12 @@ size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)
|
||||
{
|
||||
ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot write to file on virtual disc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VirtualDiscFileSystem::MkDir(const std::string &dirname)
|
||||
{
|
||||
ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot create directory on virtual disc");
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) override;
|
||||
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
||||
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
||||
void CloseFile(u32 handle) override;
|
||||
PSPFileInfo GetFileInfo(std::string filename) override;
|
||||
bool OwnsHandle(u32 handle) override;
|
||||
@@ -45,6 +46,7 @@ public:
|
||||
|
||||
// unsupported operations
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
||||
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
||||
bool MkDir(const std::string &dirname) override;
|
||||
bool RmDir(const std::string &dirname) override;
|
||||
int RenameFile(const std::string &from, const std::string &to) override;
|
||||
@@ -178,6 +180,7 @@ private:
|
||||
|
||||
std::vector<FileListEntry> fileList;
|
||||
u32 currentBlockIndex;
|
||||
u32 lastReadBlock_;
|
||||
|
||||
std::map<std::string, Handler *> handlers;
|
||||
};
|
||||
+94
-52
@@ -173,7 +173,7 @@ public:
|
||||
}
|
||||
|
||||
void DoState(PointerWrap &p) override {
|
||||
auto s = p.Section("FileNode", 1);
|
||||
auto s = p.Section("FileNode", 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
@@ -204,6 +204,9 @@ public:
|
||||
}
|
||||
|
||||
p.Do(waitingThreads);
|
||||
if (s >= 2) {
|
||||
p.Do(waitingSyncThreads);
|
||||
}
|
||||
p.Do(pausedWaits);
|
||||
}
|
||||
|
||||
@@ -229,6 +232,7 @@ public:
|
||||
PGD_DESC *pgdInfo;
|
||||
|
||||
std::vector<SceUID> waitingThreads;
|
||||
std::vector<SceUID> waitingSyncThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
// Value is actually meaningless but kept for consistency with other wait types.
|
||||
std::map<SceUID, u64> pausedWaits;
|
||||
@@ -240,7 +244,7 @@ public:
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void __IoCompleteAsyncIO(int fd);
|
||||
u64 __IoCompleteAsyncIO(FileNode *f);
|
||||
|
||||
static void TellFsThreadEnded (SceUID threadID) {
|
||||
pspFileSystem.ThreadEnded(threadID);
|
||||
@@ -279,6 +283,11 @@ static void __IoFreeFd(int fd, u32 &error) {
|
||||
HLEKernel::ResumeFromWait(f->waitingThreads[i], WAITTYPE_ASYNCIO, f->GetUID(), (int)SCE_KERNEL_ERROR_WAIT_DELETE);
|
||||
}
|
||||
|
||||
CoreTiming::UnscheduleEvent(asyncNotifyEvent, fd);
|
||||
for (size_t i = 0; i < f->waitingSyncThreads.size(); ++i) {
|
||||
CoreTiming::UnscheduleEvent(syncNotifyEvent, ((u64)f->waitingSyncThreads[i] << 32) | fd);
|
||||
}
|
||||
|
||||
// Discard any pending results.
|
||||
AsyncIOResult managerResult;
|
||||
ioManager.WaitResult(f->handle, managerResult);
|
||||
@@ -302,10 +311,9 @@ static void __IoFreeFd(int fd, u32 &error) {
|
||||
// Clearly a buffer is used, it doesn't seem like they are actually kernel objects.
|
||||
|
||||
// TODO: We don't do any of that yet.
|
||||
// For now, let's at least delay the callback mnotification.
|
||||
// For now, let's at least delay the callback notification.
|
||||
static void __IoAsyncNotify(u64 userdata, int cyclesLate) {
|
||||
int fd = (int) userdata;
|
||||
__IoCompleteAsyncIO(fd);
|
||||
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(fd, error);
|
||||
@@ -314,6 +322,21 @@ static void __IoAsyncNotify(u64 userdata, int cyclesLate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_Config.iIOTimingMethod == IOTIMING_HOST) {
|
||||
if (!ioManager.HasResult(f->handle)) {
|
||||
// Try again in another 0.5ms until the IO completes on the host.
|
||||
CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, asyncNotifyEvent, userdata);
|
||||
return;
|
||||
}
|
||||
} else if (g_Config.iIOTimingMethod == IOTIMING_REALISTIC) {
|
||||
u64 finishTicks = __IoCompleteAsyncIO(f);
|
||||
if (finishTicks > CoreTiming::GetTicks()) {
|
||||
// Reschedule for later, since we now know how long it ought to take.
|
||||
CoreTiming::ScheduleEvent(finishTicks - CoreTiming::GetTicks(), asyncNotifyEvent, userdata);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (f->waitingThreads.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -350,17 +373,36 @@ static void __IoSyncNotify(u64 userdata, int cyclesLate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_Config.iIOTimingMethod == IOTIMING_HOST) {
|
||||
if (!ioManager.HasResult(f->handle)) {
|
||||
// Try again in another 0.5ms until the IO completes on the host.
|
||||
CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, syncNotifyEvent, userdata);
|
||||
return;
|
||||
}
|
||||
} else if (g_Config.iIOTimingMethod == IOTIMING_REALISTIC) {
|
||||
u64 finishTicks = ioManager.ResultFinishTicks(f->handle);
|
||||
if (finishTicks > CoreTiming::GetTicks()) {
|
||||
// Reschedule for later when the result should finish.
|
||||
CoreTiming::ScheduleEvent(finishTicks - CoreTiming::GetTicks(), syncNotifyEvent, userdata);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
f->pendingAsyncResult = false;
|
||||
f->hasAsyncResult = false;
|
||||
|
||||
AsyncIOResult managerResult;
|
||||
if (ioManager.WaitResult(f->handle, managerResult)) {
|
||||
result = managerResult;
|
||||
result = managerResult.result;
|
||||
} else {
|
||||
ERROR_LOG(SCEIO, "Unable to complete IO operation on %s", f->GetName());
|
||||
}
|
||||
|
||||
f->pendingAsyncResult = false;
|
||||
f->hasAsyncResult = false;
|
||||
|
||||
HLEKernel::ResumeFromWait(threadID, WAITTYPE_IO, fd, result);
|
||||
f->waitingSyncThreads.erase(std::remove(f->waitingSyncThreads.begin(), f->waitingSyncThreads.end(), threadID), f->waitingSyncThreads.end());
|
||||
}
|
||||
|
||||
static void __IoAsyncBeginCallback(SceUID threadID, SceUID prevCallbackId) {
|
||||
@@ -567,22 +609,26 @@ static u32 sceKernelStderr() {
|
||||
return PSP_STDERR;
|
||||
}
|
||||
|
||||
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.
|
||||
u64 __IoCompleteAsyncIO(FileNode *f) {
|
||||
if (g_Config.iIOTimingMethod == IOTIMING_REALISTIC) {
|
||||
u64 finishTicks = ioManager.ResultFinishTicks(f->handle);
|
||||
if (finishTicks > CoreTiming::GetTicks()) {
|
||||
return finishTicks;
|
||||
}
|
||||
if (f->callbackID) {
|
||||
__KernelNotifyCallback(f->callbackID, f->callbackArg);
|
||||
}
|
||||
f->pendingAsyncResult = false;
|
||||
f->hasAsyncResult = true;
|
||||
}
|
||||
AsyncIOResult managerResult;
|
||||
if (ioManager.WaitResult(f->handle, managerResult)) {
|
||||
f->asyncResult = managerResult.result;
|
||||
} else {
|
||||
// It's okay, not all operations are deferred.
|
||||
}
|
||||
if (f->callbackID) {
|
||||
__KernelNotifyCallback(f->callbackID, f->callbackArg);
|
||||
}
|
||||
f->pendingAsyncResult = false;
|
||||
f->hasAsyncResult = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __IoCopyDate(ScePspDateTime& date_out, const tm& date_in)
|
||||
@@ -709,7 +755,13 @@ static u32 npdrmRead(FileNode *f, u8 *data, int size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __IoRead(int &result, int id, u32 data_addr, int size) {
|
||||
static bool __IoRead(int &result, int id, u32 data_addr, int size, int &us) {
|
||||
// Low estimate, may be improved later from the ReadFile result.
|
||||
us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
|
||||
if (id == PSP_STDIN) {
|
||||
DEBUG_LOG(SCEIO, "sceIoRead STDIN");
|
||||
result = 0; //stdin
|
||||
@@ -753,7 +805,7 @@ static bool __IoRead(int &result, int id, u32 data_addr, int size) {
|
||||
ioManager.ScheduleOperation(ev);
|
||||
return false;
|
||||
} else {
|
||||
result = (int) pspFileSystem.ReadFile(f->handle, data, size);
|
||||
result = (int) pspFileSystem.ReadFile(f->handle, data, size, us);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -786,19 +838,15 @@ static u32 sceIoRead(int id, u32 data_addr, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
int us;
|
||||
bool complete = __IoRead(result, id, data_addr, size, us);
|
||||
if (!complete) {
|
||||
DEBUG_LOG(SCEIO, "sceIoRead(%d, %08x, %x): deferring result", id, data_addr, size);
|
||||
|
||||
__IoSchedSync(f, id, us);
|
||||
__KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io read");
|
||||
f->waitingSyncThreads.push_back(__KernelGetCurThread());
|
||||
return 0;
|
||||
} else if (result >= 0) {
|
||||
DEBUG_LOG(SCEIO, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
|
||||
@@ -810,12 +858,7 @@ static u32 sceIoRead(int id, u32 data_addr, int size) {
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
// TODO: Technically we shouldn't read into the buffer yet...
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
if (f) {
|
||||
@@ -824,7 +867,8 @@ static u32 sceIoReadAsync(int id, u32 data_addr, int size) {
|
||||
return SCE_KERNEL_ERROR_ASYNC_BUSY;
|
||||
}
|
||||
int result;
|
||||
bool complete = __IoRead(result, id, data_addr, size);
|
||||
int us;
|
||||
bool complete = __IoRead(result, id, data_addr, size, us);
|
||||
if (complete) {
|
||||
f->asyncResult = result;
|
||||
DEBUG_LOG(SCEIO, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
|
||||
@@ -839,7 +883,13 @@ static u32 sceIoReadAsync(int id, u32 data_addr, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool __IoWrite(int &result, int id, u32 data_addr, int size) {
|
||||
static bool __IoWrite(int &result, int id, u32 data_addr, int size, int &us) {
|
||||
// Low estimate, may be improved later from the WriteFile result.
|
||||
us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
|
||||
const void *data_ptr = Memory::GetPointer(data_addr);
|
||||
// Let's handle stdout/stderr specially.
|
||||
if (id == PSP_STDOUT || id == PSP_STDERR) {
|
||||
@@ -883,7 +933,7 @@ static bool __IoWrite(int &result, int id, u32 data_addr, int size) {
|
||||
ioManager.ScheduleOperation(ev);
|
||||
return false;
|
||||
} else {
|
||||
result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size);
|
||||
result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size, us);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -907,19 +957,15 @@ static u32 sceIoWrite(int id, u32 data_addr, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Timing is probably not very accurate, low estimate.
|
||||
int us = size / 100;
|
||||
if (us < 100) {
|
||||
us = 100;
|
||||
}
|
||||
|
||||
int result;
|
||||
bool complete = __IoWrite(result, id, data_addr, size);
|
||||
int us;
|
||||
bool complete = __IoWrite(result, id, data_addr, size, us);
|
||||
if (!complete) {
|
||||
DEBUG_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): deferring result", id, data_addr, size);
|
||||
|
||||
__IoSchedSync(f, id, us);
|
||||
__KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io write");
|
||||
f->waitingSyncThreads.push_back(__KernelGetCurThread());
|
||||
return 0;
|
||||
} else if (result >= 0) {
|
||||
DEBUG_LOG(SCEIO, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size);
|
||||
@@ -940,12 +986,7 @@ static u32 sceIoWrite(int id, u32 data_addr, int size) {
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
// TODO: Technically we shouldn't read from the buffer yet...
|
||||
u32 error;
|
||||
FileNode *f = __IoGetFd(id, error);
|
||||
if (f) {
|
||||
@@ -954,7 +995,8 @@ static u32 sceIoWriteAsync(int id, u32 data_addr, int size) {
|
||||
return SCE_KERNEL_ERROR_ASYNC_BUSY;
|
||||
}
|
||||
int result;
|
||||
bool complete = __IoWrite(result, id, data_addr, size);
|
||||
int us;
|
||||
bool complete = __IoWrite(result, id, data_addr, size, us);
|
||||
if (complete) {
|
||||
f->asyncResult = result;
|
||||
DEBUG_LOG(SCEIO, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
|
||||
@@ -1366,7 +1408,7 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
|
||||
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
|
||||
}
|
||||
break;
|
||||
// TODO: What does these do? Seem to require a u32 in, no output.
|
||||
// TODO: What do these do? Seem to require a u32 in, no output.
|
||||
case 0x01F100A6:
|
||||
case 0x01F100A8:
|
||||
case 0x01F100A9:
|
||||
|
||||
@@ -47,6 +47,11 @@ void AsyncIOManager::Shutdown() {
|
||||
results_.clear();
|
||||
}
|
||||
|
||||
bool AsyncIOManager::HasResult(u32 handle) {
|
||||
lock_guard guard(resultsLock_);
|
||||
return results_.find(handle) != results_.end();
|
||||
}
|
||||
|
||||
bool AsyncIOManager::PopResult(u32 handle, AsyncIOResult &result) {
|
||||
lock_guard guard(resultsLock_);
|
||||
if (results_.find(handle) != results_.end()) {
|
||||
@@ -59,6 +64,16 @@ bool AsyncIOManager::PopResult(u32 handle, AsyncIOResult &result) {
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncIOManager::ReadResult(u32 handle, AsyncIOResult &result) {
|
||||
lock_guard guard(resultsLock_);
|
||||
if (results_.find(handle) != results_.end()) {
|
||||
result = results_[handle];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncIOManager::WaitResult(u32 handle, AsyncIOResult &result) {
|
||||
lock_guard guard(resultsLock_);
|
||||
ScheduleEvent(IO_EVENT_SYNC);
|
||||
@@ -75,6 +90,24 @@ bool AsyncIOManager::WaitResult(u32 handle, AsyncIOResult &result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 AsyncIOManager::ResultFinishTicks(u32 handle) {
|
||||
AsyncIOResult result;
|
||||
|
||||
lock_guard guard(resultsLock_);
|
||||
ScheduleEvent(IO_EVENT_SYNC);
|
||||
while (HasEvents() && ThreadEnabled() && resultsPending_.find(handle) != resultsPending_.end()) {
|
||||
if (ReadResult(handle, result)) {
|
||||
return result.finishTicks;
|
||||
}
|
||||
resultsWait_.wait_for(resultsLock_, 16);
|
||||
}
|
||||
if (ReadResult(handle, result)) {
|
||||
return result.finishTicks;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AsyncIOManager::ProcessEvent(AsyncIOEvent ev) {
|
||||
switch (ev.type) {
|
||||
case IO_EVENT_READ:
|
||||
@@ -91,14 +124,15 @@ void AsyncIOManager::ProcessEvent(AsyncIOEvent ev) {
|
||||
}
|
||||
|
||||
void AsyncIOManager::Read(u32 handle, u8 *buf, size_t bytes) {
|
||||
size_t result = pspFileSystem.ReadFile(handle, buf, bytes);
|
||||
EventResult(handle, result);
|
||||
int usec = 0;
|
||||
s64 result = pspFileSystem.ReadFile(handle, buf, bytes, usec);
|
||||
EventResult(handle, AsyncIOResult(result, usec));
|
||||
}
|
||||
|
||||
void AsyncIOManager::Write(u32 handle, u8 *buf, size_t bytes) {
|
||||
// We want to sign extend this on 32-bit.
|
||||
AsyncIOResult result = (ssize_t)pspFileSystem.WriteFile(handle, buf, bytes);
|
||||
EventResult(handle, result);
|
||||
int usec = 0;
|
||||
s64 result = pspFileSystem.WriteFile(handle, buf, bytes, usec);
|
||||
EventResult(handle, AsyncIOResult(result, usec));
|
||||
}
|
||||
|
||||
void AsyncIOManager::EventResult(u32 handle, AsyncIOResult result) {
|
||||
@@ -111,12 +145,20 @@ void AsyncIOManager::EventResult(u32 handle, AsyncIOResult result) {
|
||||
}
|
||||
|
||||
void AsyncIOManager::DoState(PointerWrap &p) {
|
||||
auto s = p.Section("AsyncIoManager", 1);
|
||||
auto s = p.Section("AsyncIoManager", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
SyncThread();
|
||||
lock_guard guard(resultsLock_);
|
||||
p.Do(resultsPending_);
|
||||
p.Do(results_);
|
||||
if (s >= 2) {
|
||||
p.Do(results_);
|
||||
} else {
|
||||
std::map<u32, size_t> oldResults;
|
||||
p.Do(oldResults);
|
||||
for (auto it = oldResults.begin(), end = oldResults.end(); it != end; ++it) {
|
||||
results_[it->first] = AsyncIOResult(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,29 @@ struct AsyncIOEvent {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Something better.
|
||||
typedef s64 AsyncIOResult;
|
||||
struct AsyncIOResult {
|
||||
AsyncIOResult() : result(0), finishTicks(0) {
|
||||
}
|
||||
|
||||
explicit AsyncIOResult(s64 r) : result(r), finishTicks(0) {
|
||||
}
|
||||
|
||||
AsyncIOResult(s64 r, int usec) : result(r) {
|
||||
finishTicks = CoreTiming::GetTicks() + usToCycles(usec);
|
||||
}
|
||||
|
||||
void DoState(PointerWrap &p) {
|
||||
auto s = p.Section("AsyncIOResult", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
p.Do(result);
|
||||
p.Do(finishTicks);
|
||||
}
|
||||
|
||||
s64 result;
|
||||
u64 finishTicks;
|
||||
};
|
||||
|
||||
typedef ThreadEventQueue<NoBase, AsyncIOEvent, AsyncIOEventType, IO_EVENT_INVALID, IO_EVENT_SYNC, IO_EVENT_FINISH> IOThreadEventQueue;
|
||||
class AsyncIOManager : public IOThreadEventQueue {
|
||||
@@ -55,8 +76,11 @@ public:
|
||||
void ScheduleOperation(AsyncIOEvent ev);
|
||||
void Shutdown();
|
||||
|
||||
bool HasResult(u32 handle);
|
||||
bool PopResult(u32 handle, AsyncIOResult &result);
|
||||
bool ReadResult(u32 handle, AsyncIOResult &result);
|
||||
bool WaitResult(u32 handle, AsyncIOResult &result);
|
||||
u64 ResultFinishTicks(u32 handle);
|
||||
|
||||
protected:
|
||||
virtual void ProcessEvent(AsyncIOEvent ref);
|
||||
|
||||
@@ -392,6 +392,9 @@ void GameSettingsScreen::CreateViews() {
|
||||
|
||||
systemSettings->Add(new CheckBox(&g_Config.bSeparateCPUThread, s->T("Multithreaded (experimental)")));
|
||||
systemSettings->Add(new CheckBox(&g_Config.bSeparateIOThread, s->T("I/O on thread (experimental)")))->SetEnabled(!PSP_IsInited());
|
||||
static const char *ioTimingMethods[] = { "Fast (lag on slow storage)", "Host (bugs, less lag)", "Simulate UMD delays" };
|
||||
View *ioTimingMethod = systemSettings->Add(new PopupMultiChoice(&g_Config.iIOTimingMethod, s->T("IO timing method"), ioTimingMethods, 0, ARRAY_SIZE(ioTimingMethods), s, screenManager()));
|
||||
ioTimingMethod->SetEnabledPtr(&g_Config.bSeparateIOThread);
|
||||
systemSettings->Add(new CheckBox(&g_Config.bForceLagSync, s->T("Force real clock sync (slower, less lag)")));
|
||||
systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, s->T("Change CPU Clock", "Change CPU Clock (0 = default) (unstable)"), screenManager()));
|
||||
#ifndef MOBILE_DEVICE
|
||||
|
||||
Reference in new issue
Block a user