From 193a336189928bd690a4e756e5fadeca787038a3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 11 Aug 2013 11:53:21 -0700 Subject: [PATCH] Run sceIoRead operations on a separate thread. This should improve stutter. Also verified via a timer to improve performance (although, not very much, ~0.07 seconds during an entire game load.) --- Core/HLE/sceIo.cpp | 131 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 29 deletions(-) diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 59692a1256..47dc36d3d4 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -103,6 +103,7 @@ 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; @@ -297,6 +298,31 @@ 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; @@ -316,6 +342,7 @@ void __IoInit() { MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED); asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify); + syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify); std::string memstickpath; std::string flash0path; @@ -348,6 +375,8 @@ void __IoDoState(PointerWrap &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"); } @@ -427,6 +456,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); } @@ -473,6 +508,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; @@ -552,7 +595,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 @@ -561,58 +604,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 > 256) { + 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);