From bf97387f26813415d0a4f56d816eeb8e50711b17 Mon Sep 17 00:00:00 2001 From: Kimmy Andersson Date: Sat, 9 Jan 2021 22:18:57 +0100 Subject: [PATCH] Improved SRAM performance (#925) * Offload NDS SRAM writing to separate thread, debounce writes to two seconds after last flush or DeInit. * Fixed printf messages. * Fixes after CR. * Fixed potential portability issue with time_t --- src/CMakeLists.txt | 1 + src/NDS.cpp | 3 + src/NDSCart.cpp | 15 ++-- src/NDSCart_SRAMManager.cpp | 147 +++++++++++++++++++++++++++++++ src/NDSCart_SRAMManager.h | 33 +++++++ src/Platform.h | 2 + src/frontend/qt_sdl/Platform.cpp | 5 +- 7 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 src/NDSCart_SRAMManager.cpp create mode 100644 src/NDSCart_SRAMManager.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f07cea6..bd1f31ed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(core STATIC melonDLDI.h NDS.cpp NDSCart.cpp + NDSCart_SRAMManager.cpp Platform.h ROMList.h RTC.cpp diff --git a/src/NDS.cpp b/src/NDS.cpp index 7131bae0..00c7389a 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -32,6 +32,7 @@ #include "Wifi.h" #include "AREngine.h" #include "Platform.h" +#include "NDSCart_SRAMManager.h" #ifdef JIT_ENABLED #include "ARMJIT.h" @@ -190,6 +191,7 @@ bool Init() DMAs[6] = new DMA(1, 2); DMAs[7] = new DMA(1, 3); + if (!NDSCart_SRAMManager::Init()) return false; if (!NDSCart::Init()) return false; if (!GBACart::Init()) return false; if (!GPU::Init()) return false; @@ -217,6 +219,7 @@ void DeInit() for (int i = 0; i < 8; i++) delete DMAs[i]; + NDSCart_SRAMManager::DeInit(); NDSCart::DeInit(); GBACart::DeInit(); GPU::DeInit(); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index fa235a4e..07652882 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -27,7 +27,7 @@ #include "Config.h" #include "ROMList.h" #include "melonDLDI.h" - +#include "NDSCart_SRAMManager.h" namespace NDSCart_SRAM { @@ -145,6 +145,8 @@ void LoadSave(const char* path, u32 type) } } + NDSCart_SRAMManager::Setup(path, SRAM, SRAMLength); + switch (SRAMLength) { case 512: WriteFunc = Write_EEPROMTiny; break; @@ -450,17 +452,10 @@ void Write(u8 val, u32 hold) void FlushSRAMFile() { - if (!SRAMFileDirty) - return; + if (!SRAMFileDirty) return; SRAMFileDirty = false; - - FILE* f = Platform::OpenFile(SRAMPath, "wb"); - if (f) - { - fwrite(SRAM, SRAMLength, 1, f); - fclose(f); - } + NDSCart_SRAMManager::RequestFlush(); } } diff --git a/src/NDSCart_SRAMManager.cpp b/src/NDSCart_SRAMManager.cpp new file mode 100644 index 00000000..88ff40b9 --- /dev/null +++ b/src/NDSCart_SRAMManager.cpp @@ -0,0 +1,147 @@ +/* + Copyright 2016-2020 Arisotura + + This file is part of melonDS. + + melonDS 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, either version 3 of the License, or (at your option) + any later version. + + melonDS 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 for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include +#include "NDSCart_SRAMManager.h" +#include "Platform.h" + +namespace NDSCart_SRAMManager +{ + Platform::Thread* FlushThread; + bool FlushThreadRunning; + Platform::Mutex* SecondaryBufferLock; + + char Path[1024]; + + u8* Buffer; + u32 Length; + + u8* SecondaryBuffer; + u32 SecondaryBufferLength; + + time_t TimeAtLastFlushRequest; + + // We keep versions in case the user closes the application before + // a flush cycle is finished. + u32 PreviousFlushVersion; + u32 FlushVersion; + + void FlushThreadFunc(); + void FlushSecondaryBufferToFile(); + + bool Init() + { + FlushThread = Platform::Thread_Create(FlushThreadFunc); + FlushThreadRunning = true; + + SecondaryBufferLock = Platform::Mutex_Create(); + + return true; + } + + void DeInit() + { + if (FlushThreadRunning) + { + FlushThreadRunning = false; + Platform::Thread_Wait(FlushThread); + Platform::Thread_Free(FlushThread); + FlushSecondaryBufferToFile(); + } + + delete SecondaryBuffer; + + Platform::Mutex_Free(SecondaryBufferLock); + } + + void Setup(const char* path, u8* buffer, u32 length) + { + // Flush SRAM in case there is unflushed data from previous state. + FlushSecondaryBufferToFile(); + + Platform::Mutex_Lock(SecondaryBufferLock); + + strncpy(Path, path, 1023); + Path[1023] = '\0'; + + Buffer = buffer; + Length = length; + + delete SecondaryBuffer; // Delete secondary buffer, there might be previous state. + + SecondaryBuffer = new u8[length]; + SecondaryBufferLength = length; + + FlushVersion = 0; + PreviousFlushVersion = 0; + TimeAtLastFlushRequest = 0; + + Platform::Mutex_Unlock(SecondaryBufferLock); + } + + void RequestFlush() + { + Platform::Mutex_Lock(SecondaryBufferLock); + printf("NDS SRAM: Flush requested\n"); + memcpy(SecondaryBuffer, Buffer, Length); + FlushVersion++; + TimeAtLastFlushRequest = time(NULL); + Platform::Mutex_Unlock(SecondaryBufferLock); + } + + void FlushThreadFunc() + { + for (;;) + { + Platform::Sleep(100 * 1000); // 100ms + + if (!FlushThreadRunning) return; + + // We debounce for two seconds after last flush request to ensure that writing has finished. + if (TimeAtLastFlushRequest == 0 || difftime(time(NULL), TimeAtLastFlushRequest) < 2) + { + continue; + } + + FlushSecondaryBufferToFile(); + } + } + + void FlushSecondaryBufferToFile() + { + if (FlushVersion == PreviousFlushVersion) + { + return; + } + + Platform::Mutex_Lock(SecondaryBufferLock); + FILE* f = Platform::OpenFile(Path, "wb"); + if (f) + { + printf("NDS SRAM: Written\n"); + fwrite(SecondaryBuffer, SecondaryBufferLength, 1, f); + fclose(f); + } + PreviousFlushVersion = FlushVersion; + TimeAtLastFlushRequest = 0; + Platform::Mutex_Unlock(SecondaryBufferLock); + } +} diff --git a/src/NDSCart_SRAMManager.h b/src/NDSCart_SRAMManager.h new file mode 100644 index 00000000..2486af29 --- /dev/null +++ b/src/NDSCart_SRAMManager.h @@ -0,0 +1,33 @@ +/* + Copyright 2016-2020 Arisotura + + This file is part of melonDS. + + melonDS 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, either version 3 of the License, or (at your option) + any later version. + + melonDS 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 for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NDSCART_SRAMMANAGER_H +#define NDSCART_SRAMMANAGER_H + +#include "types.h" + +namespace NDSCart_SRAMManager +{ + bool Init(); + void DeInit(); + + void Setup(const char* path, u8* buffer, u32 length); + void RequestFlush(); +} + +#endif // NDSCART_SRAMMANAGER_H \ No newline at end of file diff --git a/src/Platform.h b/src/Platform.h index b4dda9eb..42e1e246 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -102,6 +102,8 @@ void LAN_DeInit(); int LAN_SendPacket(u8* data, int len); int LAN_RecvPacket(u8* data); +void Sleep(u64 usecs); + } #endif // PLATFORM_H diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index d3480e44..7c4b5537 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -207,7 +207,6 @@ void Thread_Wait(Thread* thread) ((QThread*) thread)->wait(); } - Semaphore* Semaphore_Create() { return (Semaphore*)new QSemaphore(); @@ -443,5 +442,9 @@ int LAN_RecvPacket(u8* data) return LAN_Socket::RecvPacket(data); } +void Sleep(u64 usecs) +{ + QThread::usleep(usecs); +} }