From f0ec6fab4d52da7116bd3a37b02f8353fd26a164 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 21:45:57 -0800 Subject: [PATCH 1/8] Reorganize disk cache to allow for dynamic size. --- Core/FileLoaders/DiskCachingFileLoader.cpp | 31 +++++++++++++++++----- Core/FileLoaders/DiskCachingFileLoader.h | 13 ++++++--- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 73d5a37b53..1d047d2876 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -117,6 +117,7 @@ void DiskCachingFileLoaderCache::InitCache(const std::string &path) { cacheSize_ = 0; indexCount_ = 0; oldestGeneration_ = 0; + maxBlocks_ = MAX_BLOCKS_LOWER_BOUND; generation_ = 0; const std::string cacheFilePath = MakeCacheFilePath(path); @@ -248,7 +249,7 @@ size_t DiskCachingFileLoaderCache::SaveIntoCache(FileLoader *backend, s64 pos, s } bool DiskCachingFileLoaderCache::MakeCacheSpaceFor(size_t blocks) { - size_t goal = MAX_BLOCKS_CACHED - blocks; + size_t goal = (size_t)maxBlocks_ - blocks; while (cacheSize_ > goal) { u16 minGeneration = generation_; @@ -414,6 +415,9 @@ bool DiskCachingFileLoaderCache::LoadCacheFile(const std::string &path) { valid = false; } else if (header.filesize != filesize_) { valid = false; + } else if (header.maxBlocks < MAX_BLOCKS_LOWER_BOUND || header.maxBlocks > MAX_BLOCKS_UPPER_BOUND) { + // This means it's not in our safety bounds, reject. + valid = false; } // If it's valid, retain the file pointer. @@ -427,6 +431,7 @@ bool DiskCachingFileLoaderCache::LoadCacheFile(const std::string &path) { // Now let's load the index. blockSize_ = header.blockSize; + maxBlocks_ = header.maxBlocks; LoadCacheIndex(); } else { ERROR_LOG(LOADER, "Disk cache file header did not match, recreating cache file"); @@ -446,8 +451,8 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { indexCount_ = (filesize_ + blockSize_ - 1) / blockSize_; index_.resize(indexCount_); - blockIndexLookup_.resize(MAX_BLOCKS_CACHED); - memset(&blockIndexLookup_[0], INVALID_INDEX, MAX_BLOCKS_CACHED * sizeof(blockIndexLookup_[0])); + blockIndexLookup_.resize(maxBlocks_); + memset(&blockIndexLookup_[0], INVALID_INDEX, maxBlocks_ * sizeof(blockIndexLookup_[0])); if (fread(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) { fclose(f_); @@ -462,7 +467,7 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { cacheSize_ = 0; for (size_t i = 0; i < index_.size(); ++i) { - if (index_[i].block > MAX_BLOCKS_CACHED) { + if (index_[i].block > maxBlocks_) { index_[i].block = INVALID_BLOCK; } if (index_[i].block == INVALID_BLOCK) { @@ -482,6 +487,14 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { } void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { + maxBlocks_ = DetermineMaxBlocks(); + if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) { + // There's not enough free space to cache, disable. + f_ = nullptr; + ERROR_LOG(LOADER, "Not enough free space; disabling disk cache"); + return; + } + f_ = File::OpenCFile(path, "wb+"); if (!f_) { ERROR_LOG(LOADER, "Could not create disk cache file"); @@ -499,6 +512,7 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { header.version = CACHE_VERSION; header.blockSize = blockSize_; header.filesize = filesize_; + header.maxBlocks = maxBlocks_; if (fwrite(&header, sizeof(header), 1, f_) != 1) { fclose(f_); @@ -509,8 +523,8 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { indexCount_ = (filesize_ + blockSize_ - 1) / blockSize_; index_.resize(indexCount_); - blockIndexLookup_.resize(MAX_BLOCKS_CACHED); - memset(&blockIndexLookup_[0], INVALID_INDEX, MAX_BLOCKS_CACHED * sizeof(blockIndexLookup_[0])); + blockIndexLookup_.resize(maxBlocks_); + memset(&blockIndexLookup_[0], INVALID_INDEX, maxBlocks_ * sizeof(blockIndexLookup_[0])); if (fwrite(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) { fclose(f_); @@ -519,3 +533,8 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { return; } } + +u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { + // TODO (don't forget to clamp UPPER) + return 4096; +} diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index f175470a83..abec752683 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -104,24 +104,27 @@ private: void LoadCacheIndex(); void CreateCacheFile(const std::string &path); + u32 DetermineMaxBlocks(); + // File format: // 64 magic // 32 version // 32 blockSize // 64 filesize + // 32 maxBlocks // index[filesize / blockSize] <-- ~500 KB for 4GB // 32 (fileoffset - headersize) / blockSize -> -1=not present // 16 generation? // 16 hits? - // blocks[MAX_BLOCKS] + // blocks[up to maxBlocks] // 8 * blockSize enum { - CACHE_VERSION = 1, + CACHE_VERSION = 2, DEFAULT_BLOCK_SIZE = 65536, MAX_BLOCKS_PER_READ = 16, - // TODO: Dynamic. - MAX_BLOCKS_CACHED = 4096, // 256 MB + MAX_BLOCKS_LOWER_BOUND = 256, // 16 MB + MAX_BLOCKS_UPPER_BOUND = 8192, // 512 MB INVALID_BLOCK = 0xFFFFFFFF, INVALID_INDEX = 0xFFFFFFFF, }; @@ -131,6 +134,7 @@ private: u32 blockSize_; u16 generation_; u16 oldestGeneration_; + u32 maxBlocks_; size_t cacheSize_; size_t indexCount_; recursive_mutex lock_; @@ -140,6 +144,7 @@ private: u32_le version; u32_le blockSize; s64_le filesize; + s32_le maxBlocks; }; struct BlockInfo { From df6cbafe0f26cea34c8c1a3ae0a26eae1a141a63 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 21:57:35 -0800 Subject: [PATCH 2/8] Measure free disk space to size block count. --- CMakeLists.txt | 2 + Core/FileLoaders/DiskCachingFileLoader.cpp | 29 ++++++++++- Core/FileLoaders/DiskCachingFileLoader.h | 1 + Core/FileSystems/DirectoryFileSystem.cpp | 32 ++++--------- ext/native/Android.mk | 1 + ext/native/file/CMakeLists.txt | 3 +- ext/native/file/free.cpp | 56 ++++++++++++++++++++++ ext/native/file/free.h | 6 +++ ext/native/native.vcxproj | 2 + ext/native/native.vcxproj.filters | 6 +++ 10 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 ext/native/file/free.cpp create mode 100644 ext/native/file/free.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f39485a87..e5a9399462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -923,6 +923,8 @@ add_library(native STATIC ext/native/file/fd_util.h ext/native/file/file_util.cpp ext/native/file/file_util.h + ext/native/file/free.cpp + ext/native/file/free.h ext/native/file/ini_file.cpp ext/native/file/ini_file.h ext/native/file/path.cpp diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 1d047d2876..e90c0ae7d5 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -18,11 +18,13 @@ #include #include #include "file/file_util.h" +#include "file/free.h" #include "Common/FileUtil.h" #include "Core/FileLoaders/DiskCachingFileLoader.h" #include "Core/System.h" static const char *CACHEFILE_MAGIC = "ppssppDC"; +static const s64 SAFETY_FREE_DISK_SPACE = 768 * 1024 * 1024; // 768 MB std::string DiskCachingFileLoaderCache::cacheDir_; @@ -534,7 +536,30 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { } } +u64 DiskCachingFileLoaderCache::FreeDiskSpace() { + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + + uint64_t result = 0; + if (free_disk_space(dir, result)) { + return result; + } + + // We can't know for sure how much is free, so we have to assume none. + return 0; +} + u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { - // TODO (don't forget to clamp UPPER) - return 4096; + const s64 freeBytes = FreeDiskSpace(); + // We want to leave them some room for other stuff. + const u64 availBytes = std::max(0LL, freeBytes - SAFETY_FREE_DISK_SPACE); + const u64 freeBlocks = availBytes / (u64)DEFAULT_BLOCK_SIZE; + + if (freeBlocks > MAX_BLOCKS_UPPER_BOUND) { + return MAX_BLOCKS_UPPER_BOUND; + } + // Might be lower than LOWER_BOUND, but that's okay. It means not enough space. + return freeBlocks; } diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index abec752683..bf60d2c535 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -104,6 +104,7 @@ private: void LoadCacheIndex(); void CreateCacheFile(const std::string &path); + u64 FreeDiskSpace(); u32 DetermineMaxBlocks(); // File format: diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 2d52904dcd..329172b4c1 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "file/free.h" #include "file/zip_read.h" #include "i18n/i18n.h" #include "util/text/utf8.h" @@ -798,35 +799,18 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { } u64 DirectoryFileSystem::FreeSpace(const std::string &path) { -#ifdef _WIN32 - const std::wstring w32path = ConvertUTF8ToWString(GetLocalPath(path)); - ULARGE_INTEGER free; - if (GetDiskFreeSpaceExW(w32path.c_str(), &free, nullptr, nullptr)) - return free.QuadPart; -#elif defined(__SYMBIAN32__) - QSystemStorageInfo storageInfo; - return (u64)storageInfo.availableDiskSpace("E"); -#else - std::string localPath = GetLocalPath(path); - struct statvfs diskstat; - int res = statvfs(localPath.c_str(), &diskstat); + uint64_t result = 0; + if (free_disk_space(GetLocalPath(path), result)) { + return result; + } #if HOST_IS_CASE_SENSITIVE std::string fixedCase = path; - if (res != 0 && FixPathCase(basePath, fixedCase, FPC_FILE_MUST_EXIST)) { + if (FixPathCase(basePath, fixedCase, FPC_FILE_MUST_EXIST)) { // May have failed due to case sensitivity, try again. - localPath = GetLocalPath(fixedCase); - res = statvfs(localPath.c_str(), &diskstat); - } -#endif - - if (res == 0) { -#ifndef ANDROID - if (diskstat.f_flag & ST_RDONLY) { - return 0; + if (free_disk_space(GetLocalPath(fixedCase), result)) { + return result; } -#endif - return (u64)diskstat.f_bavail * (u64)diskstat.f_frsize; } #endif diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 7248e605b8..74dcec3855 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -41,6 +41,7 @@ LOCAL_SRC_FILES :=\ file/fd_util.cpp \ file/chunk_file.cpp \ file/file_util.cpp \ + file/free.cpp \ file/path.cpp \ file/ini_file.cpp \ file/zip_read.cpp \ diff --git a/ext/native/file/CMakeLists.txt b/ext/native/file/CMakeLists.txt index 047f14655b..6cb73d143a 100644 --- a/ext/native/file/CMakeLists.txt +++ b/ext/native/file/CMakeLists.txt @@ -1,7 +1,8 @@ set(SRCS chunk_file.cpp zip_read.cpp - file_util.cpp) + file_util.cpp + free.cpp) set(SRCS ${SRCS}) diff --git a/ext/native/file/free.cpp b/ext/native/file/free.cpp new file mode 100644 index 0000000000..9522fff346 --- /dev/null +++ b/ext/native/file/free.cpp @@ -0,0 +1,56 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#else +#include +#include +#include +#if defined(ANDROID) +#include +#include +#define statvfs statfs +#elif defined(__SYMBIAN32__) +#include +QTM_USE_NAMESPACE +#else +#include +#endif +#include +#include +#endif + +#include "base/basictypes.h" +#include "util/text/utf8.h" + +bool free_disk_space(const std::string &dir, uint64_t &space) { +#ifdef _WIN32 + const std::wstring w32path = ConvertUTF8ToWString(dir); + ULARGE_INTEGER free; + if (GetDiskFreeSpaceExW(w32path.c_str(), &free, nullptr, nullptr)) { + space = free.QuadPart; + return true; + } +#elif defined(__SYMBIAN32__) + QSystemStorageInfo storageInfo; + space = (uint64_t)storageInfo.availableDiskSpace("E"); + return true; +#else + struct statvfs diskstat; + int res = statvfs(dir.c_str(), &diskstat); + + if (res == 0) { +#ifndef ANDROID + if (diskstat.f_flag & ST_RDONLY) { + space = 0; + return true; + } +#endif + space = (uint64_t)diskstat.f_bavail * (uint64_t)diskstat.f_frsize; + return true; + } +#endif + + // We can't know how much is free. + return false; +} \ No newline at end of file diff --git a/ext/native/file/free.h b/ext/native/file/free.h new file mode 100644 index 0000000000..1c5690737e --- /dev/null +++ b/ext/native/file/free.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include "base/basictypes.h" + +bool free_disk_space(const std::string &dir, uint64_t &space); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index ee844615a8..426fb463df 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -226,6 +226,7 @@ + @@ -680,6 +681,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index aac8a59712..87ee09ad91 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -302,6 +302,9 @@ input + + file + @@ -736,6 +739,9 @@ base + + file + From f2cefebcb6497fd85f2ff8de469e32c639e95810 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 22:35:17 -0800 Subject: [PATCH 3/8] Garbage collect files when low on free cache space. --- Core/FileLoaders/DiskCachingFileLoader.cpp | 83 ++++++++++++++++++++-- Core/FileLoaders/DiskCachingFileLoader.h | 6 +- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index e90c0ae7d5..961b611ad8 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -16,9 +16,11 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include #include "file/file_util.h" #include "file/free.h" +#include "util/text/utf8.h" #include "Common/FileUtil.h" #include "Core/FileLoaders/DiskCachingFileLoader.h" #include "Core/System.h" @@ -86,6 +88,17 @@ size_t DiskCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data) return readSize; } +std::vector DiskCachingFileLoader::GetCachedPathsInUse() { + // This is on the file loader so that it can manage the caches_. + std::vector files; + + for (auto it : caches_) { + files.push_back(it.first); + } + + return files; +} + void DiskCachingFileLoader::InitCache() { std::string path = backend_->Path(); auto &entry = caches_[path]; @@ -326,12 +339,7 @@ u32 DiskCachingFileLoaderCache::AllocateBlock(u32 indexPos) { return INVALID_BLOCK; } -std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &path) { - std::string dir = cacheDir_; - if (dir.empty()) { - dir = GetSysDirectory(DIRECTORY_CACHE); - } - +std::string DiskCachingFileLoaderCache::MakeCacheFilename(const std::string &path) { static const char *const invalidChars = "?*:/\\^|<>\"'"; std::string filename = path; for (size_t i = 0; i < filename.size(); ++i) { @@ -341,11 +349,20 @@ std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &pat } } + return filename + ".ppdc"; +} + +std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &path) { + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + if (!File::Exists(dir)) { File::CreateFullPath(dir); } - return dir + "/" + filename; + return dir + "/" + MakeCacheFilename(path); } s64 DiskCachingFileLoaderCache::GetBlockOffset(u32 block) { @@ -490,6 +507,10 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { maxBlocks_ = DetermineMaxBlocks(); + if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) { + GarbageCollectCacheFiles(MAX_BLOCKS_LOWER_BOUND * DEFAULT_BLOCK_SIZE); + maxBlocks_ = DetermineMaxBlocks(); + } if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) { // There's not enough free space to cache, disable. f_ = nullptr; @@ -563,3 +584,51 @@ u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { // Might be lower than LOWER_BOUND, but that's okay. It means not enough space. return freeBlocks; } + +void DiskCachingFileLoaderCache::GarbageCollectCacheFiles(u64 goalBytes) { + // We attempt to free up at least enough files from the cache to get goalBytes more space. + const std::vector usedPaths = DiskCachingFileLoader::GetCachedPathsInUse(); + std::set used; + for (std::string path : usedPaths) { + used.insert(MakeCacheFilename(path)); + } + + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + + std::vector files; + getFilesInDir(dir.c_str(), &files, "ppdc:"); + + u64 remaining = goalBytes; + // TODO: Could order by LRU or etc. + for (FileInfo file : files) { + if (file.isDirectory) { + continue; + } + if (used.find(file.name) != used.end()) { + // In use, must leave alone. + continue; + } + +#ifdef _WIN32 + const std::wstring w32path = ConvertUTF8ToWString(file.fullName); + bool success = DeleteFileW(w32path.c_str()) != 0; +#else + bool success = unlink(file.fullName.c_str()) == 0; +#endif + + if (success) { + if (file.size > remaining) { + // We're done, huzzah. + break; + } + + // A little bit more. + remaining -= file.size; + } + } + + // At this point, we've done all we can. +} diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index bf60d2c535..45065fa5bd 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -47,6 +47,8 @@ public: } virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data) override; + static std::vector GetCachedPathsInUse(); + private: void InitCache(); void ShutdownCache(); @@ -100,12 +102,14 @@ private: s64 GetBlockOffset(u32 block); std::string MakeCacheFilePath(const std::string &path); + std::string MakeCacheFilename(const std::string &path); bool LoadCacheFile(const std::string &path); void LoadCacheIndex(); void CreateCacheFile(const std::string &path); u64 FreeDiskSpace(); u32 DetermineMaxBlocks(); + void GarbageCollectCacheFiles(u64 goalBytes); // File format: // 64 magic @@ -145,7 +149,7 @@ private: u32_le version; u32_le blockSize; s64_le filesize; - s32_le maxBlocks; + u32_le maxBlocks; }; struct BlockInfo { From 9bec81b9c5916f8f5c15b7839c5b6f3b5e8ffd15 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 22:35:37 -0800 Subject: [PATCH 4/8] Enable the disk cache for URLs by default. --- Core/Loaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index e6260e09b9..832fa0171e 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -36,7 +36,7 @@ FileLoader *ConstructFileLoader(const std::string &filename) { if (filename.find("http://") == 0 || filename.find("https://") == 0) - return new CachingFileLoader(new RetryingFileLoader(new HTTPFileLoader(filename))); + return new CachingFileLoader(new DiskCachingFileLoader(new RetryingFileLoader(new HTTPFileLoader(filename)))); return new LocalFileLoader(filename); } From eb5072bba58452aca1352e48ff2074e2371189f6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 22:36:13 -0800 Subject: [PATCH 5/8] Warning fixes. --- GPU/Common/GPUStateUtils.cpp | 3 +++ ext/native/file/file_util.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index fb47c103ca..513b3f2ad1 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -457,6 +457,9 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu return REPLACE_BLEND_STANDARD; } } + + // Should never get here. + return REPLACE_BLEND_STANDARD; } LogicOpReplaceType ReplaceLogicOpType() { diff --git a/ext/native/file/file_util.cpp b/ext/native/file/file_util.cpp index 8ff360665b..36a9cc8d09 100644 --- a/ext/native/file/file_util.cpp +++ b/ext/native/file/file_util.cpp @@ -269,7 +269,7 @@ size_t getFilesInDir(const char *directory, std::vector *files, const (virtualName[2] == '\0'))) continue; - // Remove dotfiles (should be made optional?) + // Remove dotfiles (optional with flag.) if (!(flags & GETFILES_GETHIDDEN) && virtualName[0] == '.') continue; From 475964133cc80861850683a42c8e0baeb7ea936e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 22:48:34 -0800 Subject: [PATCH 6/8] Make disk cache size a bit more flexible. This way it will shrink to allow a few games to be cached simultaneously. --- Core/FileLoaders/DiskCachingFileLoader.cpp | 30 +++++++++++++++++++--- Core/FileLoaders/DiskCachingFileLoader.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 961b611ad8..f5c07aa8e5 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -27,6 +27,8 @@ static const char *CACHEFILE_MAGIC = "ppssppDC"; static const s64 SAFETY_FREE_DISK_SPACE = 768 * 1024 * 1024; // 768 MB +// Aim to allow this many files cached at once. +static const u32 CACHE_SPACE_FLEX = 4; std::string DiskCachingFileLoaderCache::cacheDir_; @@ -578,13 +580,35 @@ u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { const u64 availBytes = std::max(0LL, freeBytes - SAFETY_FREE_DISK_SPACE); const u64 freeBlocks = availBytes / (u64)DEFAULT_BLOCK_SIZE; - if (freeBlocks > MAX_BLOCKS_UPPER_BOUND) { - return MAX_BLOCKS_UPPER_BOUND; + const u32 alreadyCachedCount = CountCachedFiles(); + // This is how many more files of free space we will aim for. + const u32 flex = CACHE_SPACE_FLEX > alreadyCachedCount ? CACHE_SPACE_FLEX - alreadyCachedCount : 1; + + const u64 freeBlocksWithFlex = freeBlocks / flex; + if (freeBlocksWithFlex > MAX_BLOCKS_LOWER_BOUND) { + if (freeBlocksWithFlex > MAX_BLOCKS_UPPER_BOUND) { + return MAX_BLOCKS_UPPER_BOUND; + } + // This might be smaller than what's free, but if they try to launch a second game, + // they'll be happier when it can be cached too. + return freeBlocksWithFlex; } - // Might be lower than LOWER_BOUND, but that's okay. It means not enough space. + + // Might be lower than LOWER_BOUND, but that's okay. That means not enough space. + // We abandon the idea of flex since there's not enough space free anyway. return freeBlocks; } +u32 DiskCachingFileLoaderCache::CountCachedFiles() { + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + + std::vector files; + return (u32)getFilesInDir(dir.c_str(), &files, "ppdc:"); +} + void DiskCachingFileLoaderCache::GarbageCollectCacheFiles(u64 goalBytes) { // We attempt to free up at least enough files from the cache to get goalBytes more space. const std::vector usedPaths = DiskCachingFileLoader::GetCachedPathsInUse(); diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index 45065fa5bd..5f647a7925 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -109,6 +109,7 @@ private: u64 FreeDiskSpace(); u32 DetermineMaxBlocks(); + u32 CountCachedFiles(); void GarbageCollectCacheFiles(u64 goalBytes); // File format: From 0264422b8d23efb1a3ef519a7aecbf68eb11665c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 22:56:46 -0800 Subject: [PATCH 7/8] Add a bit more error detection to the disk cache. --- Core/FileLoaders/DiskCachingFileLoader.cpp | 68 ++++++++++++++++++---- Core/FileLoaders/DiskCachingFileLoader.h | 2 +- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index f5c07aa8e5..5e6b2e22b3 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -165,6 +165,10 @@ void DiskCachingFileLoaderCache::ShutdownCache() { size_t DiskCachingFileLoaderCache::ReadFromCache(s64 pos, size_t bytes, void *data) { lock_guard guard(lock_); + if (!f_) { + return 0; + } + s64 cacheStartPos = pos / blockSize_; s64 cacheEndPos = (pos + bytes - 1) / blockSize_; size_t readSize = 0; @@ -182,7 +186,9 @@ size_t DiskCachingFileLoaderCache::ReadFromCache(s64 pos, size_t bytes, void *da } size_t toRead = std::min(bytes - readSize, (size_t)blockSize_ - offset); - ReadBlockData(p + readSize, info, offset, toRead); + if (!ReadBlockData(p + readSize, info, offset, toRead)) { + return readSize; + } readSize += toRead; // Don't need an offset after the first read. @@ -194,6 +200,11 @@ size_t DiskCachingFileLoaderCache::ReadFromCache(s64 pos, size_t bytes, void *da size_t DiskCachingFileLoaderCache::SaveIntoCache(FileLoader *backend, s64 pos, size_t bytes, void *data) { lock_guard guard(lock_); + if (!f_) { + // Just to keep things working. + return backend->ReadAt(pos, bytes, data); + } + s64 cacheStartPos = pos / blockSize_; s64 cacheEndPos = (pos + bytes - 1) / blockSize_; size_t readSize = 0; @@ -374,49 +385,84 @@ s64 DiskCachingFileLoaderCache::GetBlockOffset(u32 block) { return blockOffset + (s64)block * (s64)blockSize_; } -void DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size) { +bool DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size) { + if (!f_) { + return false; + } s64 blockOffset = GetBlockOffset(info.block); + bool failed = false; #ifdef ANDROID if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } else if (read(fd_, dest + offset, size) != (ssize_t)size) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } #else if (fseeko(f_, blockOffset, SEEK_SET) != 0) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } else if (fread(dest + offset, size, 1, f_) != 1) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } #endif + + if (failed) { + ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + fclose(f_); + f_ = nullptr; + fd_ = 0; + } + return !failed; } void DiskCachingFileLoaderCache::WriteBlockData(BlockInfo &info, u8 *src) { + if (!f_) { + return; + } s64 blockOffset = GetBlockOffset(info.block); + bool failed = false; #ifdef ANDROID if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } else if (write(fd_, src, blockSize_) != (ssize_t)blockSize_) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } #else if (fseeko(f_, blockOffset, SEEK_SET) != 0) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } else if (fwrite(src, blockSize_, 1, f_) != 1) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } #endif + + if (failed) { + ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + fclose(f_); + f_ = nullptr; + fd_ = 0; + } } void DiskCachingFileLoaderCache::WriteIndexData(u32 indexPos, BlockInfo &info) { + if (!f_) { + return; + } + u32 offset = (u32)sizeof(FileHeader) + indexPos * (u32)sizeof(BlockInfo); + bool failed = false; if (fseek(f_, offset, SEEK_SET) != 0) { - ERROR_LOG(LOADER, "Unable to write disk cache index entry."); + failed = true; } else if (fwrite(&info, sizeof(BlockInfo), 1, f_) != 1) { + failed = true; + } + + if (failed) { ERROR_LOG(LOADER, "Unable to write disk cache index entry."); + fclose(f_); + f_ = nullptr; + fd_ = 0; } } diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index 5f647a7925..d250ab5e2e 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -96,7 +96,7 @@ private: u32 AllocateBlock(u32 indexPos); struct BlockInfo; - void ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size); + bool ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size); void WriteBlockData(BlockInfo &info, u8 *src); void WriteIndexData(u32 indexPos, BlockInfo &info); s64 GetBlockOffset(u32 block); From 723a2c98aff151297c975495a321747bcbbda1ed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 18 Nov 2015 22:03:35 -0800 Subject: [PATCH 8/8] Minor warning fix. --- Core/HW/SasReverb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HW/SasReverb.cpp b/Core/HW/SasReverb.cpp index 81125a545c..c1d148342c 100644 --- a/Core/HW/SasReverb.cpp +++ b/Core/HW/SasReverb.cpp @@ -228,7 +228,7 @@ void SasReverb::ProcessReverb(int16_t *output, const int16_t *input, size_t inpu // This runs at 22khz. // Very unoptimized, straight from the description. Can probably be reformulated into something way more efficient. // Or we could actually template the whole thing with the parameters as template arguments, as the presets are fixed. - for (int i = 0; i < inputSize; i++) { + for (size_t i = 0; i < inputSize; i++) { // Dividing by two here is an incorrect hack. Some multiplication factor is needed to prevent the reverb from getting too loud, though. int16_t LeftInput = input[i * 2] >> 1; int16_t RightInput = input[i * 2 + 1] >> 1;