diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index c076f94aa3..ff485df67a 100644 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -214,8 +214,12 @@ void CGameView::RegisterInit() LDR_Register([](const double) { - return g_TexMan.LoadTerrainTextures(); - }, L"LoadTerrainTextures", 60); + return g_TexMan.StartTerrainTextures(); + }, L"StartTerrainTextures", 1); + LDR_Register([](const double) + { + return g_TexMan.PollTerrainTextures(); + }, L"PollTerrainTextures", 60); } void CGameView::BeginFrame() diff --git a/source/graphics/TerrainTextureManager.cpp b/source/graphics/TerrainTextureManager.cpp index 7562dd2d5b..e6216e1c59 100644 --- a/source/graphics/TerrainTextureManager.cpp +++ b/source/graphics/TerrainTextureManager.cpp @@ -30,6 +30,7 @@ #include "lib/path.h" #include "lib/status.h" #include "lib/tex/tex.h" +#include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/XMB/XMBStorage.h" @@ -121,10 +122,40 @@ static Status AddTextureCallback(const VfsPath& pathname, const CFileInfo&, cons return INFO::OK; } -int CTerrainTextureManager::LoadTerrainTextures() +struct CTerrainTextureManager::LoadTexturesState { - AddTextureCallbackData data = {this, CTerrainPropertiesPtr(new CTerrainProperties(CTerrainPropertiesPtr()))}; - vfs::ForEachFile(g_VFS, L"art/terrains/", AddTextureCallback, (uintptr_t)&data, L"*.xml", vfs::DIR_RECURSIVE, AddTextureDirCallback, (uintptr_t)&data); + vfs::ForEachFileContext context; + AddTextureCallbackData data; + + LoadTexturesState(const VfsPath& startPath, CTerrainTextureManager* self) + : context{startPath}, data{self, std::make_shared(CTerrainPropertiesPtr())} {} +}; + +int CTerrainTextureManager::StartTerrainTextures() +{ + m_LoadTexturesState = std::make_unique(VfsPath{L"art/terrains/"}, this); + return 0; +} + +int CTerrainTextureManager::PollTerrainTextures() +{ + LoadTexturesState& state{*m_LoadTexturesState}; + const size_t numberOfDirectoriesToLoadPerCall{10}; + for (size_t iteration{0}; !state.context.empty() && iteration < numberOfDirectoriesToLoadPerCall; ++iteration) + { + vfs::ForEachFileNext(state.context, g_VFS, AddTextureCallback, (uintptr_t)&state.data, L"*.xml", vfs::DIR_RECURSIVE, AddTextureDirCallback, (uintptr_t)&state.data); + } + + if (!state.context.empty()) + { + // We don't know exact number so just using a rough approximation of the + // current number. + const size_t totalApproximateAmountOfTextures{1000}; + return Clamp(m_TextureEntries.size() * 90 / totalApproximateAmountOfTextures, 10, 100); + } + + m_LoadTexturesState.reset(); + return 0; } diff --git a/source/graphics/TerrainTextureManager.h b/source/graphics/TerrainTextureManager.h index 83a6f0aad9..194fc832a5 100644 --- a/source/graphics/TerrainTextureManager.h +++ b/source/graphics/TerrainTextureManager.h @@ -99,7 +99,8 @@ public: // Find all XML's in the directory (with subdirs) and try to load them as // terrain XML's - int LoadTerrainTextures(); + int StartTerrainTextures(); + int PollTerrainTextures(); void UnloadTerrainTextures(); diff --git a/source/lib/file/vfs/vfs_util.cpp b/source/lib/file/vfs/vfs_util.cpp index 1f259ca56b..66da5c5cf1 100644 --- a/source/lib/file/vfs/vfs_util.cpp +++ b/source/lib/file/vfs/vfs_util.cpp @@ -60,40 +60,41 @@ Status GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, Status ForEachFile(const PIVFS& fs, const VfsPath& startPath, FileCallback cb, uintptr_t cbData, const wchar_t* pattern, size_t flags, DirCallback dircb, uintptr_t dircbData) { - // (declare here to avoid reallocations) - CFileInfos files; - DirectoryNames subdirectoryNames; - - // (a FIFO queue is more efficient than recursion because it uses less - // stack space and avoids seeks due to breadth-first traversal.) - std::queue pendingDirectories; - pendingDirectories.push(startPath/""); - while(!pendingDirectories.empty()) + ForEachFileContext context{startPath}; + while(!context.pendingDirectories.empty()) { - const VfsPath& path = pendingDirectories.front(); - - RETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &files, &subdirectoryNames)); - - if(dircb) - RETURN_STATUS_IF_ERR(dircb(path, dircbData)); - - for(size_t i = 0; i < files.size(); i++) - { - const CFileInfo fileInfo = files[i]; - if(!match_wildcard(fileInfo.Name().string().c_str(), pattern)) - continue; - - const VfsPath pathname(path / fileInfo.Name()); // (CFileInfo only stores the name) - RETURN_STATUS_IF_ERR(cb(pathname, fileInfo, cbData)); - } - - if(!(flags & DIR_RECURSIVE)) - break; - - for(size_t i = 0; i < subdirectoryNames.size(); i++) - pendingDirectories.push(path / subdirectoryNames[i]/""); - pendingDirectories.pop(); + RETURN_STATUS_IF_ERR(ForEachFileNext(context, fs, cb, cbData, pattern, flags, dircb, dircbData)); } + return INFO::OK; +} + +Status ForEachFileNext( + ForEachFileContext& context, const PIVFS& fs, FileCallback cb, + uintptr_t cbData, const wchar_t* pattern, size_t flags, + DirCallback dircb, uintptr_t dircbData) +{ + const VfsPath path{std::move(context.pendingDirectories.front())}; + context.pendingDirectories.pop(); + + RETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &context.files, &context.subdirectoryNames)); + + if(dircb) + RETURN_STATUS_IF_ERR(dircb(path, dircbData)); + + for (const CFileInfo& fileInfo : context.files) + { + if(!match_wildcard(fileInfo.Name().string().c_str(), pattern)) + continue; + + const VfsPath pathname(path / fileInfo.Name()); // (CFileInfo only stores the name) + RETURN_STATUS_IF_ERR(cb(pathname, fileInfo, cbData)); + } + + if(!(flags & DIR_RECURSIVE)) + return INFO::OK; + + for(const OsPath& subdirectoryName : context.subdirectoryNames) + context.pendingDirectories.push(path / subdirectoryName / ""); return INFO::OK; } diff --git a/source/lib/file/vfs/vfs_util.h b/source/lib/file/vfs/vfs_util.h index 18628d9a56..f90f7f189f 100644 --- a/source/lib/file/vfs/vfs_util.h +++ b/source/lib/file/vfs/vfs_util.h @@ -34,8 +34,10 @@ #include #include +#include -namespace vfs { +namespace vfs +{ extern Status GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, VfsPaths& pathnames); @@ -84,8 +86,24 @@ enum DirFlags * @param dircbData * @return Status **/ -extern Status ForEachFile(const PIVFS& fs, const VfsPath& path, FileCallback cb, uintptr_t cbData, const wchar_t* pattern = 0, size_t flags = 0, DirCallback dircb = NULL, uintptr_t dircbData = 0); +Status ForEachFile(const PIVFS& fs, const VfsPath& path, FileCallback cb, uintptr_t cbData, const wchar_t* pattern = 0, size_t flags = 0, DirCallback dircb = NULL, uintptr_t dircbData = 0); +struct ForEachFileContext +{ + // (declare here to avoid reallocations) + CFileInfos files; + DirectoryNames subdirectoryNames; + + // (a FIFO queue is more efficient than recursion because it uses less + // stack space and avoids seeks due to breadth-first traversal.) + std::queue pendingDirectories; + + ForEachFileContext(const VfsPath& startPath) { pendingDirectories.push(startPath / ""); } + + bool empty() const { return pendingDirectories.empty(); } +}; +// The same ForEachFile but allows to pause the iteration. +Status ForEachFileNext(ForEachFileContext& context, const PIVFS& fs, FileCallback cb, uintptr_t cbData, const wchar_t* pattern = 0, size_t flags = 0, DirCallback dircb = NULL, uintptr_t dircbData = 0); /** * Determine the next available pathname with a given format. @@ -100,7 +118,7 @@ extern Status ForEachFile(const PIVFS& fs, const VfsPath& path, FileCallback cb, * If 0, numbers corresponding to existing files are skipped. * @param nextPathname receives the output. **/ -extern void NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname); +void NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname); } // namespace vfs