2026-03-24 16:14:05 +08:00
|
|
|
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
|
|
|
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
|
|
|
|
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
2026-04-02 03:03:36 +08:00
|
|
|
#include <XCEngine/Core/IO/ResourceFileSystem.h>
|
2026-03-27 00:30:49 +08:00
|
|
|
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
2026-03-28 19:26:08 +08:00
|
|
|
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
2026-03-27 00:30:49 +08:00
|
|
|
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
2026-03-28 19:26:08 +08:00
|
|
|
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
2026-03-17 19:38:27 +08:00
|
|
|
|
|
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace Resources {
|
|
|
|
|
|
2026-03-27 00:30:49 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
template<typename TLoader>
|
|
|
|
|
void RegisterBuiltinLoader(ResourceManager& manager, TLoader& loader) {
|
|
|
|
|
if (manager.GetLoader(loader.GetResourceType()) == nullptr) {
|
|
|
|
|
manager.RegisterLoader(&loader);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MaterialLoader g_materialLoader;
|
2026-03-28 19:26:08 +08:00
|
|
|
MeshLoader g_meshLoader;
|
2026-03-27 00:30:49 +08:00
|
|
|
ShaderLoader g_shaderLoader;
|
2026-03-28 19:26:08 +08:00
|
|
|
TextureLoader g_textureLoader;
|
2026-03-27 00:30:49 +08:00
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2026-03-17 19:38:27 +08:00
|
|
|
ResourceManager& ResourceManager::Get() {
|
|
|
|
|
static ResourceManager instance;
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::Initialize() {
|
2026-04-02 03:03:36 +08:00
|
|
|
if (m_asyncLoader) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 19:38:27 +08:00
|
|
|
m_asyncLoader = Core::MakeUnique<AsyncLoader>();
|
|
|
|
|
m_asyncLoader->Initialize(2);
|
2026-03-27 00:30:49 +08:00
|
|
|
|
|
|
|
|
RegisterBuiltinLoader(*this, g_materialLoader);
|
2026-03-28 19:26:08 +08:00
|
|
|
RegisterBuiltinLoader(*this, g_meshLoader);
|
2026-03-27 00:30:49 +08:00
|
|
|
RegisterBuiltinLoader(*this, g_shaderLoader);
|
2026-03-28 19:26:08 +08:00
|
|
|
RegisterBuiltinLoader(*this, g_textureLoader);
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::Shutdown() {
|
|
|
|
|
UnloadAll();
|
2026-04-02 03:03:36 +08:00
|
|
|
if (m_asyncLoader) {
|
|
|
|
|
m_asyncLoader->Shutdown();
|
|
|
|
|
m_asyncLoader.reset();
|
|
|
|
|
}
|
|
|
|
|
m_assetDatabase.Shutdown();
|
|
|
|
|
ResourceFileSystem::Get().Shutdown();
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::SetResourceRoot(const Containers::String& rootPath) {
|
|
|
|
|
m_resourceRoot = rootPath;
|
2026-04-02 03:03:36 +08:00
|
|
|
if (!m_resourceRoot.Empty()) {
|
|
|
|
|
ResourceFileSystem::Get().Initialize(rootPath);
|
|
|
|
|
m_assetDatabase.Initialize(rootPath);
|
|
|
|
|
} else {
|
|
|
|
|
ResourceFileSystem::Get().Shutdown();
|
|
|
|
|
m_assetDatabase.Shutdown();
|
|
|
|
|
}
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Containers::String& ResourceManager::GetResourceRoot() const {
|
|
|
|
|
return m_resourceRoot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::AddRef(ResourceGUID guid) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
auto* it = m_refCounts.Find(guid);
|
|
|
|
|
if (it == nullptr) {
|
|
|
|
|
m_refCounts.Insert(guid, 1);
|
|
|
|
|
} else {
|
|
|
|
|
(*it)++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_resourceCache.Contains(guid)) {
|
|
|
|
|
ReloadResource(guid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::Release(ResourceGUID guid) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
auto* it = m_refCounts.Find(guid);
|
|
|
|
|
if (it != nullptr) {
|
|
|
|
|
(*it)--;
|
|
|
|
|
|
|
|
|
|
if (*it == 0) {
|
|
|
|
|
m_refCounts.Erase(guid);
|
|
|
|
|
m_cache.OnZeroRefCount(guid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::uint32 ResourceManager::GetRefCount(ResourceGUID guid) const {
|
|
|
|
|
auto* it = m_refCounts.Find(guid);
|
|
|
|
|
return it != nullptr ? *it : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::RegisterLoader(IResourceLoader* loader) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
2026-03-17 22:32:27 +08:00
|
|
|
m_loaders.Insert(loader->GetResourceType(), loader);
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IResourceLoader* ResourceManager::GetLoader(ResourceType type) const {
|
|
|
|
|
auto* it = m_loaders.Find(type);
|
2026-03-17 22:32:27 +08:00
|
|
|
return it != nullptr ? *it : nullptr;
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IResourceLoader* ResourceManager::FindLoader(ResourceType type) {
|
|
|
|
|
return GetLoader(type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IResource* ResourceManager::FindInCache(ResourceGUID guid) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
auto* it = m_resourceCache.Find(guid);
|
|
|
|
|
return it != nullptr ? *it : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::AddToCache(ResourceGUID guid, IResource* resource) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
2026-04-02 03:03:36 +08:00
|
|
|
|
|
|
|
|
resource->m_guid = guid;
|
2026-03-17 19:38:27 +08:00
|
|
|
m_resourceCache.Insert(guid, resource);
|
|
|
|
|
m_memoryUsage += resource->GetMemorySize();
|
|
|
|
|
m_cache.Add(guid, resource);
|
|
|
|
|
|
|
|
|
|
if (m_memoryUsage > m_memoryBudget) {
|
|
|
|
|
m_cache.OnMemoryPressure(m_memoryUsage - m_memoryBudget);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::Unload(ResourceGUID guid) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
|
|
|
|
|
auto* it = m_resourceCache.Find(guid);
|
|
|
|
|
if (it != nullptr) {
|
|
|
|
|
IResource* resource = *it;
|
|
|
|
|
m_resourceCache.Erase(guid);
|
2026-04-02 03:03:36 +08:00
|
|
|
m_guidToPath.Erase(guid);
|
2026-03-17 19:38:27 +08:00
|
|
|
m_memoryUsage -= resource->GetMemorySize();
|
|
|
|
|
resource->Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::UnloadAll() {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
2026-04-02 03:03:36 +08:00
|
|
|
|
|
|
|
|
const auto cachedResources = m_resourceCache.GetPairs();
|
|
|
|
|
for (const auto& pair : cachedResources) {
|
|
|
|
|
if (pair.second != nullptr) {
|
|
|
|
|
pair.second->Release();
|
|
|
|
|
}
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
m_resourceCache.Clear();
|
|
|
|
|
m_refCounts.Clear();
|
2026-04-02 03:03:36 +08:00
|
|
|
m_guidToPath.Clear();
|
2026-03-17 19:38:27 +08:00
|
|
|
m_memoryUsage = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::SetMemoryBudget(size_t bytes) {
|
|
|
|
|
m_memoryBudget = bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t ResourceManager::GetMemoryUsage() const {
|
|
|
|
|
return m_memoryUsage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t ResourceManager::GetMemoryBudget() const {
|
|
|
|
|
return m_memoryBudget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::FlushCache() {
|
|
|
|
|
m_cache.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IResource* ResourceManager::Find(const Containers::String& path) {
|
|
|
|
|
return Find(ResourceGUID::Generate(path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IResource* ResourceManager::Find(ResourceGUID guid) {
|
|
|
|
|
return FindInCache(guid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ResourceManager::Exists(const Containers::String& path) const {
|
|
|
|
|
return Exists(ResourceGUID::Generate(path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ResourceManager::Exists(ResourceGUID guid) const {
|
|
|
|
|
return m_resourceCache.Contains(guid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Containers::String ResourceManager::ResolvePath(const Containers::String& relativePath) const {
|
|
|
|
|
return m_resourceRoot + "/" + relativePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::LoadAsync(const Containers::String& path, ResourceType type,
|
|
|
|
|
std::function<void(LoadResult)> callback) {
|
|
|
|
|
LoadAsync(path, type, nullptr, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::LoadAsync(const Containers::String& path, ResourceType type,
|
|
|
|
|
ImportSettings* settings,
|
|
|
|
|
std::function<void(LoadResult)> callback) {
|
|
|
|
|
m_asyncLoader->Submit(path, type, settings, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::Unload(const Containers::String& path) {
|
|
|
|
|
Unload(ResourceGUID::Generate(path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::UnloadUnused() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::UnregisterLoader(ResourceType type) {
|
|
|
|
|
m_loaders.Erase(type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::ReloadResource(ResourceGUID guid) {
|
|
|
|
|
auto* pathIt = m_guidToPath.Find(guid);
|
|
|
|
|
if (pathIt == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-04-02 03:03:36 +08:00
|
|
|
|
|
|
|
|
const Containers::String path = *pathIt;
|
|
|
|
|
auto* typeIt = m_resourceCache.Find(guid);
|
|
|
|
|
(void)typeIt;
|
2026-03-17 19:38:27 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 03:20:18 +08:00
|
|
|
Containers::Array<Containers::String> ResourceManager::GetResourcePaths() const {
|
|
|
|
|
Containers::Array<Containers::String> paths;
|
|
|
|
|
for (const auto& pair : m_guidToPath) {
|
2026-03-18 03:37:34 +08:00
|
|
|
paths.PushBack(pair.second);
|
2026-03-18 03:20:18 +08:00
|
|
|
}
|
|
|
|
|
return paths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResourceManager::UnloadGroup(const Containers::Array<ResourceGUID>& guids) {
|
|
|
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
|
for (const auto& guid : guids) {
|
|
|
|
|
auto* it = m_resourceCache.Find(guid);
|
|
|
|
|
if (it != nullptr) {
|
|
|
|
|
IResource* resource = *it;
|
|
|
|
|
m_resourceCache.Erase(guid);
|
2026-04-02 03:03:36 +08:00
|
|
|
m_guidToPath.Erase(guid);
|
2026-03-18 03:20:18 +08:00
|
|
|
m_memoryUsage -= resource->GetMemorySize();
|
|
|
|
|
resource->Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 03:03:36 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 19:38:27 +08:00
|
|
|
} // namespace Resources
|
|
|
|
|
} // namespace XCEngine
|