1
0
forked from mirrors/0ad

Makes loading terrain textures more smooth

This commit is contained in:
Vladislav Belov
2025-10-13 00:14:45 +02:00
parent fb98f5059a
commit fb94cc704f
5 changed files with 96 additions and 41 deletions
+6 -2
View File
@@ -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()
+34 -3
View File
@@ -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<CTerrainProperties>(CTerrainPropertiesPtr())} {}
};
int CTerrainTextureManager::StartTerrainTextures()
{
m_LoadTexturesState = std::make_unique<LoadTexturesState>(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<int>(m_TextureEntries.size() * 90 / totalApproximateAmountOfTextures, 10, 100);
}
m_LoadTexturesState.reset();
return 0;
}
+2 -1
View File
@@ -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();
+33 -32
View File
@@ -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<VfsPath> 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;
}
+21 -3
View File
@@ -34,8 +34,10 @@
#include <cstddef>
#include <cstdint>
#include <queue>
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<VfsPath> 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