feat(Resources): 添加资源系统基础框架
- ResourceTypes: 资源类型枚举、ResourceGUID生成 - IResource: 资源基类接口 - ResourceHandle: 资源句柄智能指针 - IResourceLoader: 加载器接口 - ResourceManager: 资源管理器(单例模式) - ResourceCache: LRU缓存实现 - AsyncLoader: 异步加载器 - 测试框架: test_resource_types, test_resource_guid Note: 当前与现有容器API存在编译差异,需要后续修复
This commit is contained in:
103
engine/src/Resources/AsyncLoader.cpp
Normal file
103
engine/src/Resources/AsyncLoader.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "Resources/AsyncLoader.h"
|
||||
#include "Resources/ResourceManager.h"
|
||||
#include "Resources/ResourceTypes.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
Core::uint64 LoadRequest::GenerateRequestId() {
|
||||
static std::atomic<Core::uint64> s_requestId{0};
|
||||
return ++s_requestId;
|
||||
}
|
||||
|
||||
AsyncLoader& AsyncLoader::Get() {
|
||||
static AsyncLoader instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void AsyncLoader::Initialize(Core::uint32 workerThreadCount) {
|
||||
(void)workerThreadCount;
|
||||
}
|
||||
|
||||
void AsyncLoader::Shutdown() {
|
||||
CancelAll();
|
||||
}
|
||||
|
||||
void AsyncLoader::Submit(const Containers::String& path, ResourceType type,
|
||||
std::function<void(LoadResult)> callback) {
|
||||
Submit(path, type, nullptr, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncLoader::Submit(const Containers::String& path, ResourceType type, ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback) {
|
||||
LoadRequest request(path, type, std::move(callback), settings);
|
||||
SubmitInternal(request);
|
||||
}
|
||||
|
||||
void AsyncLoader::SubmitInternal(LoadRequest& request) {
|
||||
IResourceLoader* loader = FindLoader(request.type);
|
||||
|
||||
if (!loader) {
|
||||
if (request.callback) {
|
||||
LoadResult result(Containers::String("No loader for type: ") +
|
||||
GetResourceTypeName(request.type));
|
||||
request.callback(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_queueMutex);
|
||||
m_pendingQueue.PushBack(request);
|
||||
m_pendingCount++;
|
||||
m_totalRequested++;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncLoader::Update() {
|
||||
Containers::Array<LoadRequest> completed;
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_completedMutex);
|
||||
completed = std::move(m_completedQueue);
|
||||
m_completedQueue.Clear();
|
||||
}
|
||||
|
||||
for (auto& request : completed) {
|
||||
m_pendingCount--;
|
||||
|
||||
if (request.callback) {
|
||||
LoadResult result(true);
|
||||
request.callback(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float AsyncLoader::GetProgress() const {
|
||||
if (m_totalRequested == 0) return 1.0f;
|
||||
return static_cast<float>(m_totalRequested - m_pendingCount.load()) / m_totalRequested;
|
||||
}
|
||||
|
||||
void AsyncLoader::CancelAll() {
|
||||
std::lock_guard lock(m_queueMutex);
|
||||
m_pendingQueue.Clear();
|
||||
m_pendingCount = 0;
|
||||
}
|
||||
|
||||
void AsyncLoader::Cancel(Core::uint64 requestId) {
|
||||
std::lock_guard lock(m_queueMutex);
|
||||
(void)requestId;
|
||||
}
|
||||
|
||||
IResourceLoader* AsyncLoader::FindLoader(ResourceType type) const {
|
||||
return ResourceManager::Get().GetLoader(type);
|
||||
}
|
||||
|
||||
void AsyncLoader::QueueCompleted(LoadRequest request, LoadResult result) {
|
||||
std::lock_guard lock(m_completedMutex);
|
||||
(void)request;
|
||||
(void)result;
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
141
engine/src/Resources/ResourceCache.cpp
Normal file
141
engine/src/Resources/ResourceCache.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "Resources/ResourceCache.h"
|
||||
#include "Resources/ResourceManager.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
static Core::uint64 g_currentTick = 0;
|
||||
|
||||
Core::uint64 CacheEntry::GetCurrentTick() {
|
||||
return ++g_currentTick;
|
||||
}
|
||||
|
||||
CacheEntry::CacheEntry(IResource* res, size_t size)
|
||||
: resource(res), guid(res->GetGUID()), memorySize(size),
|
||||
lastAccessTime(GetCurrentTick()), accessCount(1) {}
|
||||
|
||||
ResourceCache::ResourceCache() = default;
|
||||
ResourceCache::~ResourceCache() = default;
|
||||
|
||||
void ResourceCache::Add(ResourceGUID guid, IResource* resource) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_cache.Contains(guid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheEntry entry(resource, resource->GetMemorySize());
|
||||
m_cache.Insert(guid, entry);
|
||||
m_lruOrder.PushBack(guid);
|
||||
m_memoryUsage += entry.memorySize;
|
||||
}
|
||||
|
||||
void ResourceCache::Remove(ResourceGUID guid) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto* it = m_cache.Find(guid);
|
||||
if (it != nullptr) {
|
||||
m_memoryUsage -= it->memorySize;
|
||||
m_cache.Erase(guid);
|
||||
|
||||
// Simple workaround: rebuild LRU list without the guid
|
||||
Containers::Array<ResourceGUID> newList;
|
||||
for (size_t i = 0; i < m_lruOrder.Size(); ++i) {
|
||||
if (m_lruOrder[i] != guid) {
|
||||
newList.PushBack(m_lruOrder[i]);
|
||||
}
|
||||
}
|
||||
m_lruOrder = std::move(newList);
|
||||
}
|
||||
}
|
||||
|
||||
IResource* ResourceCache::Find(ResourceGUID guid) const {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto* it = m_cache.Find(guid);
|
||||
return it != nullptr ? it->resource : nullptr;
|
||||
}
|
||||
|
||||
void ResourceCache::Touch(ResourceGUID guid) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
auto* it = m_cache.Find(guid);
|
||||
if (it != nullptr) {
|
||||
it->lastAccessTime = CacheEntry::GetCurrentTick();
|
||||
it->accessCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceCache::SetMemoryBudget(size_t bytes) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_memoryBudget = bytes;
|
||||
}
|
||||
|
||||
void ResourceCache::OnMemoryPressure(size_t requiredBytes) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_memoryUsage + requiredBytes <= m_memoryBudget) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t targetRelease = (m_memoryUsage + requiredBytes) - m_memoryBudget;
|
||||
Evict(targetRelease);
|
||||
}
|
||||
|
||||
void ResourceCache::OnZeroRefCount(ResourceGUID guid) {
|
||||
|
||||
}
|
||||
|
||||
void ResourceCache::Evict(size_t requiredBytes) {
|
||||
size_t released = 0;
|
||||
|
||||
// Simple eviction: remove from end of LRU list
|
||||
while (released < requiredBytes && m_lruOrder.Size() > 0) {
|
||||
ResourceGUID guid = m_lruOrder.Back();
|
||||
m_lruOrder.PopBack();
|
||||
|
||||
auto* it = m_cache.Find(guid);
|
||||
if (it != nullptr) {
|
||||
m_memoryUsage -= it->memorySize;
|
||||
released += it->memorySize;
|
||||
it->resource->Release();
|
||||
m_cache.Erase(guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceCache::Flush() {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// Release all resources
|
||||
for (size_t i = 0; i < m_cache.Size(); ++i) {
|
||||
// Would need iteration support
|
||||
}
|
||||
|
||||
m_cache.Clear();
|
||||
m_lruOrder.Clear();
|
||||
m_memoryUsage = 0;
|
||||
}
|
||||
|
||||
void ResourceCache::Clear() {
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_cache.Clear();
|
||||
m_lruOrder.Clear();
|
||||
m_memoryUsage = 0;
|
||||
}
|
||||
|
||||
Containers::Array<ResourceGUID> ResourceCache::GetLRUList(size_t count) const {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
Containers::Array<ResourceGUID> result;
|
||||
size_t n = std::min(count, static_cast<size_t>(m_lruOrder.Size()));
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
result.PushBack(m_lruOrder[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
190
engine/src/Resources/ResourceManager.cpp
Normal file
190
engine/src/Resources/ResourceManager.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
#include "Resources/ResourceManager.h"
|
||||
#include "Resources/ResourceHandle.h"
|
||||
#include "Resources/ResourceTypes.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
ResourceManager& ResourceManager::Get() {
|
||||
static ResourceManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ResourceManager::Initialize() {
|
||||
m_asyncLoader = Core::MakeUnique<AsyncLoader>();
|
||||
m_asyncLoader->Initialize(2);
|
||||
}
|
||||
|
||||
void ResourceManager::Shutdown() {
|
||||
UnloadAll();
|
||||
m_asyncLoader->Shutdown();
|
||||
m_asyncLoader.reset();
|
||||
}
|
||||
|
||||
void ResourceManager::SetResourceRoot(const Containers::String& rootPath) {
|
||||
m_resourceRoot = rootPath;
|
||||
}
|
||||
|
||||
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);
|
||||
m_loaders.Insert(loader->GetResourceType(), Core::MakeUnique<IResourceLoader>(loader));
|
||||
}
|
||||
|
||||
IResourceLoader* ResourceManager::GetLoader(ResourceType type) const {
|
||||
auto* it = m_loaders.Find(type);
|
||||
return it != nullptr ? it->get() : nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
m_memoryUsage -= resource->GetMemorySize();
|
||||
resource->Release();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
m_resourceCache.Clear();
|
||||
m_refCounts.Clear();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
29
engine/src/Resources/ResourceTypes.cpp
Normal file
29
engine/src/Resources/ResourceTypes.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "Resources/ResourceTypes.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
ResourceGUID ResourceGUID::Generate(const char* path) {
|
||||
Core::uint64 hash = 14695981039346656037ULL;
|
||||
|
||||
while (*path) {
|
||||
hash ^= static_cast<Core::uint64>(*path);
|
||||
hash *= 1099511628211ULL;
|
||||
path++;
|
||||
}
|
||||
|
||||
return ResourceGUID(hash);
|
||||
}
|
||||
|
||||
ResourceGUID ResourceGUID::Generate(const Containers::String& path) {
|
||||
return Generate(path.CStr());
|
||||
}
|
||||
|
||||
Containers::String ResourceGUID::ToString() const {
|
||||
char buffer[17];
|
||||
snprintf(buffer, sizeof(buffer), "%016llx", value);
|
||||
return Containers::String(buffer);
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user