Implement initial Unity-style asset library cache

This commit is contained in:
2026-04-02 03:03:36 +08:00
parent 619856ab22
commit 4c167bec0e
29 changed files with 4818 additions and 73 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
#include <XCEngine/Core/Asset/AssetGUID.h>
#include <cstdio>
#include <random>
namespace XCEngine {
namespace Resources {
namespace {
Core::uint64 FNV1a64(const Core::uint8* bytes, size_t size, Core::uint64 seed) {
Core::uint64 hash = seed;
for (size_t index = 0; index < size; ++index) {
hash ^= static_cast<Core::uint64>(bytes[index]);
hash *= 1099511628211ULL;
}
return hash;
}
int HexDigitToInt(char ch) {
if (ch >= '0' && ch <= '9') {
return ch - '0';
}
if (ch >= 'a' && ch <= 'f') {
return 10 + (ch - 'a');
}
if (ch >= 'A' && ch <= 'F') {
return 10 + (ch - 'A');
}
return -1;
}
bool ParseHex64(const char* text, Core::uint64& outValue) {
outValue = 0;
for (int index = 0; index < 16; ++index) {
const int digit = HexDigitToInt(text[index]);
if (digit < 0) {
return false;
}
outValue = (outValue << 4) | static_cast<Core::uint64>(digit);
}
return true;
}
} // namespace
AssetGUID AssetGUID::Generate() {
static std::random_device rd;
static std::mt19937_64 generator(rd());
AssetGUID guid;
do {
guid.high = generator();
guid.low = generator();
} while (!guid.IsValid());
return guid;
}
bool AssetGUID::TryParse(const Containers::String& text, AssetGUID& outGuid) {
if (text.Length() != 32) {
return false;
}
Core::uint64 highValue = 0;
Core::uint64 lowValue = 0;
if (!ParseHex64(text.CStr(), highValue) ||
!ParseHex64(text.CStr() + 16, lowValue)) {
return false;
}
outGuid = AssetGUID(highValue, lowValue);
return true;
}
AssetGUID AssetGUID::ParseOrDefault(const Containers::String& text) {
AssetGUID guid;
if (TryParse(text, guid)) {
return guid;
}
return AssetGUID();
}
Containers::String AssetGUID::ToString() const {
char buffer[33] = {};
#if defined(_MSC_VER)
std::snprintf(buffer, sizeof(buffer), "%016llx%016llx",
static_cast<unsigned long long>(high),
static_cast<unsigned long long>(low));
#else
std::snprintf(buffer, sizeof(buffer), "%016lx%016lx",
static_cast<unsigned long>(high),
static_cast<unsigned long>(low));
#endif
return Containers::String(buffer);
}
AssetGUID HashBytesToAssetGUID(const void* data, size_t size) {
if (data == nullptr || size == 0) {
return AssetGUID();
}
const auto* bytes = static_cast<const Core::uint8*>(data);
const Core::uint64 highHash = FNV1a64(bytes, size, 14695981039346656037ULL);
const Core::uint64 lowHash = FNV1a64(bytes, size, 1099511628211ULL ^ 0x9e3779b97f4a7c15ULL);
return AssetGUID(highHash, lowHash);
}
AssetGUID HashStringToAssetGUID(const Containers::String& text) {
return HashBytesToAssetGUID(text.CStr(), text.Length());
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,6 +1,7 @@
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/IO/ResourceFileSystem.h>
#include <XCEngine/Resources/Material/MaterialLoader.h>
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Resources/Shader/ShaderLoader.h>
@@ -31,6 +32,10 @@ ResourceManager& ResourceManager::Get() {
}
void ResourceManager::Initialize() {
if (m_asyncLoader) {
return;
}
m_asyncLoader = Core::MakeUnique<AsyncLoader>();
m_asyncLoader->Initialize(2);
@@ -42,12 +47,23 @@ void ResourceManager::Initialize() {
void ResourceManager::Shutdown() {
UnloadAll();
m_asyncLoader->Shutdown();
m_asyncLoader.reset();
if (m_asyncLoader) {
m_asyncLoader->Shutdown();
m_asyncLoader.reset();
}
m_assetDatabase.Shutdown();
ResourceFileSystem::Get().Shutdown();
}
void ResourceManager::SetResourceRoot(const Containers::String& rootPath) {
m_resourceRoot = rootPath;
if (!m_resourceRoot.Empty()) {
ResourceFileSystem::Get().Initialize(rootPath);
m_assetDatabase.Initialize(rootPath);
} else {
ResourceFileSystem::Get().Shutdown();
m_assetDatabase.Shutdown();
}
}
const Containers::String& ResourceManager::GetResourceRoot() const {
@@ -111,7 +127,8 @@ IResource* ResourceManager::FindInCache(ResourceGUID guid) {
void ResourceManager::AddToCache(ResourceGUID guid, IResource* resource) {
std::lock_guard lock(m_mutex);
resource->m_guid = guid;
m_resourceCache.Insert(guid, resource);
m_memoryUsage += resource->GetMemorySize();
m_cache.Add(guid, resource);
@@ -128,6 +145,7 @@ void ResourceManager::Unload(ResourceGUID guid) {
if (it != nullptr) {
IResource* resource = *it;
m_resourceCache.Erase(guid);
m_guidToPath.Erase(guid);
m_memoryUsage -= resource->GetMemorySize();
resource->Release();
}
@@ -135,13 +153,16 @@ void ResourceManager::Unload(ResourceGUID guid) {
void ResourceManager::UnloadAll() {
std::lock_guard lock(m_mutex);
for (size_t i = 0; i < m_resourceCache.Size(); ++i) {
// This is a simplified approach - we'd need a way to iterate
// For now, just clear everything
const auto cachedResources = m_resourceCache.GetPairs();
for (const auto& pair : cachedResources) {
if (pair.second != nullptr) {
pair.second->Release();
}
}
m_resourceCache.Clear();
m_refCounts.Clear();
m_guidToPath.Clear();
m_memoryUsage = 0;
}
@@ -209,6 +230,10 @@ void ResourceManager::ReloadResource(ResourceGUID guid) {
if (pathIt == nullptr) {
return;
}
const Containers::String path = *pathIt;
auto* typeIt = m_resourceCache.Find(guid);
(void)typeIt;
}
Containers::Array<Containers::String> ResourceManager::GetResourcePaths() const {
@@ -226,11 +251,60 @@ void ResourceManager::UnloadGroup(const Containers::Array<ResourceGUID>& guids)
if (it != nullptr) {
IResource* resource = *it;
m_resourceCache.Erase(guid);
m_guidToPath.Erase(guid);
m_memoryUsage -= resource->GetMemorySize();
resource->Release();
}
}
}
void ResourceManager::RefreshAssetDatabase() {
if (!m_resourceRoot.Empty()) {
m_assetDatabase.Refresh();
}
}
bool ResourceManager::TryGetAssetRef(const Containers::String& path, ResourceType resourceType, AssetRef& outRef) const {
return m_assetDatabase.TryGetAssetRef(path, resourceType, outRef);
}
LoadResult ResourceManager::LoadResource(const Containers::String& path,
ResourceType type,
ImportSettings* settings) {
const ResourceGUID guid = ResourceGUID::Generate(path);
if (IResource* cached = FindInCache(guid)) {
return LoadResult(cached);
}
IResourceLoader* loader = FindLoader(type);
if (loader == nullptr) {
Debug::Logger::Get().Warning(Debug::LogCategory::FileSystem,
Containers::String("No loader found for resource type: ") +
GetResourceTypeName(type));
return LoadResult(false, "Loader not found");
}
Containers::String loadPath = path;
AssetDatabase::ResolvedAsset resolvedAsset;
if (!m_resourceRoot.Empty() &&
m_assetDatabase.EnsureArtifact(path, type, resolvedAsset) &&
resolvedAsset.artifactReady) {
loadPath = resolvedAsset.artifactMainPath;
}
LoadResult result = loader->Load(loadPath, settings);
if (!result || result.resource == nullptr) {
Debug::Logger::Get().Error(Debug::LogCategory::FileSystem,
Containers::String("Failed to load resource: ") + path + " - " + result.errorMessage);
return result;
}
result.resource->m_path = path;
AddToCache(guid, result.resource);
m_guidToPath.Insert(guid, path);
return result;
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -1,28 +1,57 @@
#include <XCEngine/Core/IO/IResourceLoader.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <filesystem>
#include <fstream>
namespace XCEngine {
namespace Resources {
Containers::Array<Core::uint8> IResourceLoader::ReadFileData(const Containers::String& path) {
namespace {
Containers::Array<Core::uint8> TryReadFileData(
const std::filesystem::path& filePath,
bool& opened) {
Containers::Array<Core::uint8> data;
std::ifstream file(path.CStr(), std::ios::binary | std::ios::ate);
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
opened = false;
return data;
}
std::streamsize size = file.tellg();
opened = true;
const std::streamsize size = file.tellg();
if (size <= 0) {
return data;
}
file.seekg(0, std::ios::beg);
data.Resize(static_cast<size_t>(size));
if (!file.read(reinterpret_cast<char*>(data.Data()), size)) {
data.Clear();
}
return data;
}
} // namespace
Containers::Array<Core::uint8> IResourceLoader::ReadFileData(const Containers::String& path) {
bool opened = false;
const std::filesystem::path inputPath(path.CStr());
Containers::Array<Core::uint8> data = TryReadFileData(inputPath, opened);
if (opened || path.Empty() || inputPath.is_absolute()) {
return data;
}
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
if (resourceRoot.Empty()) {
return data;
}
return TryReadFileData(std::filesystem::path(resourceRoot.CStr()) / inputPath, opened);
}
Containers::String IResourceLoader::GetExtension(const Containers::String& path) {
Containers::String ext;
size_t dotPos = Containers::String::npos;