diff --git a/Core/Config.cpp b/Core/Config.cpp index 128ab7179f..31cbb554df 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -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), diff --git a/Core/Config.h b/Core/Config.h index 7aebc374d3..46779cef23 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -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; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 3bf4328b6d..292484296e 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -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; } diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 78d06d9d72..e13cf5a733 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -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; diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index 4f402b5a6a..c9f6b5290a 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -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;} diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index f11ff1c7cb..a5303278a5 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -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; + } } diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index 084546f1a2..5e46436ff1 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -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_; -}; \ No newline at end of file +}; diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index cc403b64ef..d2f8df140d 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -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; } diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index e1f99e21c0..5990abded4 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -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; } diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index f94f370474..5f50b77e82 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -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"); diff --git a/Core/FileSystems/VirtualDiscFileSystem.h b/Core/FileSystems/VirtualDiscFileSystem.h index fca50d73cf..19840bf127 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.h +++ b/Core/FileSystems/VirtualDiscFileSystem.h @@ -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 fileList; u32 currentBlockIndex; + u32 lastReadBlock_; std::map handlers; }; diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 6b2d4cee80..fbee4f5133 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -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 waitingThreads; + std::vector 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 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: diff --git a/Core/HW/AsyncIOManager.cpp b/Core/HW/AsyncIOManager.cpp index c022f16404..e4bbde36ca 100644 --- a/Core/HW/AsyncIOManager.cpp +++ b/Core/HW/AsyncIOManager.cpp @@ -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 oldResults; + p.Do(oldResults); + for (auto it = oldResults.begin(), end = oldResults.end(); it != end; ++it) { + results_[it->first] = AsyncIOResult(it->second); + } + } } diff --git a/Core/HW/AsyncIOManager.h b/Core/HW/AsyncIOManager.h index 76d6f5e7f9..c2ebb68166 100644 --- a/Core/HW/AsyncIOManager.h +++ b/Core/HW/AsyncIOManager.h @@ -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 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); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b134433370..673082d144 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -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