feat(Resources): 添加资源系统基础框架
- ResourceTypes: 资源类型枚举、ResourceGUID生成 - IResource: 资源基类接口 - ResourceHandle: 资源句柄智能指针 - IResourceLoader: 加载器接口 - ResourceManager: 资源管理器(单例模式) - ResourceCache: LRU缓存实现 - AsyncLoader: 异步加载器 - 测试框架: test_resource_types, test_resource_guid Note: 当前与现有容器API存在编译差异,需要后续修复
This commit is contained in:
70
engine/include/XCEngine/Resources/AsyncLoader.h
Normal file
70
engine/include/XCEngine/Resources/AsyncLoader.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "IResourceLoader.h"
|
||||
#include "../Containers/Array.h"
|
||||
#include "../Threading/Mutex.h"
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
struct LoadRequest {
|
||||
Containers::String path;
|
||||
ResourceType type;
|
||||
std::function<void(LoadResult)> callback;
|
||||
Core::UniqueRef<ImportSettings> settings;
|
||||
Core::uint64 requestId;
|
||||
|
||||
LoadRequest() : requestId(0) {}
|
||||
|
||||
LoadRequest(const Containers::String& p, ResourceType t,
|
||||
std::function<void(LoadResult)> cb, ImportSettings* s = nullptr)
|
||||
: path(p), type(t), callback(std::move(cb)),
|
||||
settings(s), requestId(GenerateRequestId()) {}
|
||||
|
||||
private:
|
||||
static Core::uint64 GenerateRequestId();
|
||||
};
|
||||
|
||||
class AsyncLoader {
|
||||
public:
|
||||
static AsyncLoader& Get();
|
||||
|
||||
void Initialize(Core::uint32 workerThreadCount = 2);
|
||||
void Shutdown();
|
||||
|
||||
void Submit(const Containers::String& path, ResourceType type,
|
||||
std::function<void(LoadResult)> callback);
|
||||
void Submit(const Containers::String& path, ResourceType type, ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback);
|
||||
|
||||
void Update();
|
||||
bool IsLoading() const { return m_pendingCount > 0; }
|
||||
Core::uint32 GetPendingCount() const { return m_pendingCount; }
|
||||
float GetProgress() const;
|
||||
void CancelAll();
|
||||
void Cancel(Core::uint64 requestId);
|
||||
|
||||
private:
|
||||
AsyncLoader() = default;
|
||||
~AsyncLoader() = default;
|
||||
|
||||
void SubmitInternal(LoadRequest& request);
|
||||
IResourceLoader* FindLoader(ResourceType type) const;
|
||||
|
||||
void QueueCompleted(LoadRequest request, LoadResult result);
|
||||
|
||||
Threading::Mutex m_queueMutex;
|
||||
Containers::Array<LoadRequest> m_pendingQueue;
|
||||
|
||||
Threading::Mutex m_completedMutex;
|
||||
Containers::Array<LoadRequest> m_completedQueue;
|
||||
|
||||
std::atomic<Core::uint32> m_pendingCount{0};
|
||||
std::atomic<Core::uint32> m_completedCount{0};
|
||||
Core::uint32 m_totalRequested = 0;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
47
engine/include/XCEngine/Resources/IResource.h
Normal file
47
engine/include/XCEngine/Resources/IResource.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "ResourceTypes.h"
|
||||
#include "../Containers/String.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class IResource {
|
||||
public:
|
||||
virtual ~IResource() = default;
|
||||
|
||||
virtual ResourceType GetType() const = 0;
|
||||
virtual const Containers::String& GetName() const = 0;
|
||||
virtual const Containers::String& GetPath() const = 0;
|
||||
virtual ResourceGUID GetGUID() const = 0;
|
||||
virtual bool IsValid() const = 0;
|
||||
virtual size_t GetMemorySize() const = 0;
|
||||
virtual void Release() = 0;
|
||||
|
||||
protected:
|
||||
struct ConstructParams {
|
||||
Containers::String name;
|
||||
Containers::String path;
|
||||
ResourceGUID guid;
|
||||
size_t memorySize = 0;
|
||||
};
|
||||
|
||||
void Initialize(const ConstructParams& params) {
|
||||
m_name = params.name;
|
||||
m_path = params.path;
|
||||
m_guid = params.guid;
|
||||
m_memorySize = params.memorySize;
|
||||
m_isValid = true;
|
||||
}
|
||||
|
||||
void SetInvalid() { m_isValid = false; }
|
||||
|
||||
Containers::String m_name;
|
||||
Containers::String m_path;
|
||||
ResourceGUID m_guid;
|
||||
bool m_isValid = false;
|
||||
size_t m_memorySize = 0;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
61
engine/include/XCEngine/Resources/IResourceLoader.h
Normal file
61
engine/include/XCEngine/Resources/IResourceLoader.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "IResource.h"
|
||||
#include "ResourceTypes.h"
|
||||
#include "../Containers/String.h"
|
||||
#include "../Containers/Array.h"
|
||||
#include <functional>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
struct LoadResult {
|
||||
IResource* resource = nullptr;
|
||||
bool success = false;
|
||||
Containers::String errorMessage;
|
||||
|
||||
LoadResult() = default;
|
||||
explicit LoadResult(IResource* res) : resource(res), success(res != nullptr) {}
|
||||
explicit LoadResult(const Containers::String& error) : success(false), errorMessage(error) {}
|
||||
explicit LoadResult(bool inSuccess, const Containers::String& error = "")
|
||||
: success(inSuccess), errorMessage(error) {}
|
||||
|
||||
operator bool() const { return success && resource != nullptr; }
|
||||
};
|
||||
|
||||
class IResourceLoader {
|
||||
public:
|
||||
virtual ~IResourceLoader() = default;
|
||||
|
||||
virtual ResourceType GetResourceType() const = 0;
|
||||
virtual Containers::Array<Containers::String> GetSupportedExtensions() const = 0;
|
||||
virtual bool CanLoad(const Containers::String& path) const = 0;
|
||||
virtual LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) = 0;
|
||||
|
||||
virtual void LoadAsync(const Containers::String& path,
|
||||
const ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback) {
|
||||
LoadResult result = Load(path, settings);
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ImportSettings* GetDefaultSettings() const = 0;
|
||||
|
||||
protected:
|
||||
static Containers::Array<Core::uint8> ReadFileData(const Containers::String& path);
|
||||
static Containers::String GetExtension(const Containers::String& path);
|
||||
};
|
||||
|
||||
#define REGISTER_RESOURCE_LOADER(loaderType) \
|
||||
namespace { \
|
||||
struct loaderType##Registrar { \
|
||||
loaderType##Registrar() { \
|
||||
ResourceManager::Get().RegisterLoader(new loaderType()); \
|
||||
} \
|
||||
} g_##loaderType##Registrar; \
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
64
engine/include/XCEngine/Resources/ResourceCache.h
Normal file
64
engine/include/XCEngine/Resources/ResourceCache.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "ResourceTypes.h"
|
||||
#include "../Containers/HashMap.h"
|
||||
#include "../Containers/Array.h"
|
||||
#include "../Threading/Mutex.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class IResource;
|
||||
|
||||
struct CacheEntry {
|
||||
IResource* resource;
|
||||
ResourceGUID guid;
|
||||
size_t memorySize;
|
||||
Core::uint64 lastAccessTime;
|
||||
Core::uint32 accessCount;
|
||||
|
||||
CacheEntry() : resource(nullptr), memorySize(0), lastAccessTime(0), accessCount(0) {}
|
||||
CacheEntry(IResource* res, size_t size);
|
||||
|
||||
static Core::uint64 GetCurrentTick();
|
||||
};
|
||||
|
||||
class ResourceCache {
|
||||
public:
|
||||
ResourceCache();
|
||||
~ResourceCache();
|
||||
|
||||
void Add(ResourceGUID guid, IResource* resource);
|
||||
void Remove(ResourceGUID guid);
|
||||
IResource* Find(ResourceGUID guid) const;
|
||||
void Touch(ResourceGUID guid);
|
||||
|
||||
size_t GetSize() const { return m_cache.Size(); }
|
||||
size_t GetMemoryUsage() const { return m_memoryUsage; }
|
||||
|
||||
void SetMemoryBudget(size_t bytes);
|
||||
size_t GetMemoryBudget() const { return m_memoryBudget; }
|
||||
|
||||
void OnMemoryPressure(size_t requiredBytes);
|
||||
void OnZeroRefCount(ResourceGUID guid);
|
||||
void Flush();
|
||||
void Clear();
|
||||
|
||||
Containers::Array<ResourceGUID> GetLRUList(size_t count) const;
|
||||
|
||||
private:
|
||||
void Evict(size_t requiredBytes);
|
||||
void UpdateMemoryStats();
|
||||
|
||||
Containers::HashMap<ResourceGUID, CacheEntry> m_cache;
|
||||
Containers::Array<ResourceGUID> m_lruOrder;
|
||||
size_t m_memoryUsage = 0;
|
||||
size_t m_memoryBudget = 512 * 1024 * 1024;
|
||||
|
||||
Threading::Mutex m_mutex;
|
||||
mutable Threading::Mutex m_cacheMutex;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
104
engine/include/XCEngine/Resources/ResourceHandle.h
Normal file
104
engine/include/XCEngine/Resources/ResourceHandle.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include "IResource.h"
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
// Forward declaration
|
||||
class ResourceManager;
|
||||
|
||||
template<typename T>
|
||||
class ResourceHandle {
|
||||
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
|
||||
|
||||
public:
|
||||
ResourceHandle() = default;
|
||||
|
||||
explicit ResourceHandle(T* resource)
|
||||
: m_resource(resource) {
|
||||
if (m_resource) {
|
||||
ResourceManager::Get().AddRef(m_resource->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
ResourceHandle(const ResourceHandle& other)
|
||||
: m_resource(other.m_resource) {
|
||||
if (m_resource) {
|
||||
ResourceManager::Get().AddRef(m_resource->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
ResourceHandle(ResourceHandle&& other) noexcept
|
||||
: m_resource(other.m_resource) {
|
||||
other.m_resource = nullptr;
|
||||
}
|
||||
|
||||
~ResourceHandle() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
ResourceHandle& operator=(const ResourceHandle& other) {
|
||||
if (this != &other) {
|
||||
Reset();
|
||||
m_resource = other.m_resource;
|
||||
if (m_resource) {
|
||||
ResourceManager::Get().AddRef(m_resource->GetGUID());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResourceHandle& operator=(ResourceHandle&& other) noexcept {
|
||||
if (this != &other) {
|
||||
Reset();
|
||||
m_resource = other.m_resource;
|
||||
other.m_resource = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* Get() const { return m_resource; }
|
||||
T* operator->() const { return m_resource; }
|
||||
T& operator*() const { return *m_resource; }
|
||||
|
||||
bool IsValid() const { return m_resource != nullptr && m_resource->IsValid(); }
|
||||
explicit operator bool() const { return IsValid(); }
|
||||
|
||||
ResourceGUID GetGUID() const {
|
||||
return m_resource ? m_resource->GetGUID() : ResourceGUID(0);
|
||||
}
|
||||
|
||||
ResourceType GetResourceType() const {
|
||||
return m_resource ? m_resource->GetType() : ResourceType::Unknown;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if (m_resource) {
|
||||
ResourceManager::Get().Release(m_resource->GetGUID());
|
||||
m_resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Swap(ResourceHandle& other) {
|
||||
std::swap(m_resource, other.m_resource);
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_resource = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool operator==(const ResourceHandle<T>& lhs, const ResourceHandle<T>& rhs) {
|
||||
return lhs.GetGUID() == rhs.GetGUID();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(const ResourceHandle<T>& lhs, const ResourceHandle<T>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
114
engine/include/XCEngine/Resources/ResourceManager.h
Normal file
114
engine/include/XCEngine/Resources/ResourceManager.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include "IResourceLoader.h"
|
||||
#include "ResourceCache.h"
|
||||
#include "AsyncLoader.h"
|
||||
#include "ResourceHandle.h"
|
||||
#include "../Containers/String.h"
|
||||
#include "../Containers/Array.h"
|
||||
#include "../Containers/HashMap.h"
|
||||
#include "../Threading/Mutex.h"
|
||||
#include "../Debug/Logger.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
static ResourceManager& Get();
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
void SetResourceRoot(const Containers::String& rootPath);
|
||||
const Containers::String& GetResourceRoot() const;
|
||||
|
||||
template<typename T>
|
||||
ResourceHandle<T> Load(const Containers::String& path, ImportSettings* settings = nullptr) {
|
||||
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
|
||||
|
||||
ResourceGUID guid = ResourceGUID::Generate(path);
|
||||
|
||||
IResource* cached = FindInCache(guid);
|
||||
if (cached) {
|
||||
return ResourceHandle<T>(static_cast<T*>(cached));
|
||||
}
|
||||
|
||||
IResourceLoader* loader = FindLoader(GetResourceType<T>());
|
||||
if (!loader) {
|
||||
Debug::Logger::Get().Warning(Debug::LogCategory::FileSystem,
|
||||
Containers::String("No loader found for resource type: ") +
|
||||
GetResourceTypeName(GetResourceType<T>()));
|
||||
return ResourceHandle<T>();
|
||||
}
|
||||
|
||||
LoadResult result = loader->Load(path, settings);
|
||||
if (!result) {
|
||||
Debug::Logger::Get().Error(Debug::LogCategory::FileSystem,
|
||||
Containers::String("Failed to load resource: ") + path + " - " + result.errorMessage);
|
||||
return ResourceHandle<T>();
|
||||
}
|
||||
|
||||
AddToCache(guid, result.resource);
|
||||
|
||||
return ResourceHandle<T>(static_cast<T*>(result.resource));
|
||||
}
|
||||
|
||||
void LoadAsync(const Containers::String& path, ResourceType type,
|
||||
std::function<void(LoadResult)> callback);
|
||||
void LoadAsync(const Containers::String& path, ResourceType type, ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback);
|
||||
|
||||
void Unload(const Containers::String& path);
|
||||
void Unload(ResourceGUID guid);
|
||||
void UnloadUnused();
|
||||
void UnloadAll();
|
||||
|
||||
void AddRef(ResourceGUID guid);
|
||||
void Release(ResourceGUID guid);
|
||||
Core::uint32 GetRefCount(ResourceGUID guid) const;
|
||||
|
||||
void RegisterLoader(IResourceLoader* loader);
|
||||
void UnregisterLoader(ResourceType type);
|
||||
IResourceLoader* GetLoader(ResourceType type) const;
|
||||
|
||||
void SetMemoryBudget(size_t bytes);
|
||||
size_t GetMemoryUsage() const;
|
||||
size_t GetMemoryBudget() const;
|
||||
void FlushCache();
|
||||
|
||||
IResource* Find(const Containers::String& path);
|
||||
IResource* Find(ResourceGUID guid);
|
||||
bool Exists(const Containers::String& path) const;
|
||||
bool Exists(ResourceGUID guid) const;
|
||||
|
||||
Containers::String ResolvePath(const Containers::String& relativePath) const;
|
||||
|
||||
private:
|
||||
ResourceManager() = default;
|
||||
~ResourceManager() = default;
|
||||
|
||||
IResource* FindInCache(ResourceGUID guid);
|
||||
void AddToCache(ResourceGUID guid, IResource* resource);
|
||||
IResourceLoader* FindLoader(ResourceType type);
|
||||
void ReloadResource(ResourceGUID guid);
|
||||
|
||||
Containers::String m_resourceRoot;
|
||||
Containers::HashMap<ResourceGUID, IResource*> m_resourceCache;
|
||||
Containers::HashMap<ResourceGUID, Core::uint32> m_refCounts;
|
||||
Containers::HashMap<ResourceGUID, Containers::String> m_guidToPath;
|
||||
Containers::HashMap<ResourceType, Core::UniqueRef<IResourceLoader>> m_loaders;
|
||||
|
||||
size_t m_memoryUsage = 0;
|
||||
size_t m_memoryBudget = 512 * 1024 * 1024;
|
||||
|
||||
ResourceCache m_cache;
|
||||
Core::UniqueRef<AsyncLoader> m_asyncLoader;
|
||||
Threading::Mutex m_mutex;
|
||||
|
||||
friend class ResourceHandleBase;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
87
engine/include/XCEngine/Resources/ResourceTypes.h
Normal file
87
engine/include/XCEngine/Resources/ResourceTypes.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Core/Types.h"
|
||||
#include "../Core/SmartPtr.h"
|
||||
#include "../Containers/String.h"
|
||||
#include "../Containers/Array.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class ResourceType : Core::uint8 {
|
||||
Unknown = 0,
|
||||
Texture,
|
||||
Mesh,
|
||||
Material,
|
||||
Shader,
|
||||
AudioClip,
|
||||
Binary,
|
||||
AnimationClip,
|
||||
Skeleton,
|
||||
Font,
|
||||
ParticleSystem,
|
||||
Scene,
|
||||
Prefab
|
||||
};
|
||||
|
||||
constexpr const char* GetResourceTypeName(ResourceType type) {
|
||||
switch (type) {
|
||||
case ResourceType::Texture: return "Texture";
|
||||
case ResourceType::Mesh: return "Mesh";
|
||||
case ResourceType::Material: return "Material";
|
||||
case ResourceType::Shader: return "Shader";
|
||||
case ResourceType::AudioClip: return "AudioClip";
|
||||
case ResourceType::Binary: return "Binary";
|
||||
case ResourceType::AnimationClip: return "AnimationClip";
|
||||
case ResourceType::Skeleton: return "Skeleton";
|
||||
case ResourceType::Font: return "Font";
|
||||
case ResourceType::ParticleSystem: return "ParticleSystem";
|
||||
case ResourceType::Scene: return "Scene";
|
||||
case ResourceType::Prefab: return "Prefab";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct ResourceGUID {
|
||||
Core::uint64 value;
|
||||
|
||||
ResourceGUID() : value(0) {}
|
||||
explicit ResourceGUID(Core::uint64 v) : value(v) {}
|
||||
|
||||
bool IsValid() const { return value != 0; }
|
||||
|
||||
bool operator==(const ResourceGUID& other) const { return value == other.value; }
|
||||
bool operator!=(const ResourceGUID& other) const { return value != other.value; }
|
||||
|
||||
static ResourceGUID Generate(const char* path);
|
||||
static ResourceGUID Generate(const Containers::String& path);
|
||||
|
||||
Containers::String ToString() const;
|
||||
};
|
||||
|
||||
inline ResourceGUID MakeResourceGUID(const char* path) {
|
||||
return ResourceGUID::Generate(path);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ResourceType GetResourceType();
|
||||
|
||||
template<> inline ResourceType GetResourceType<class Texture>() { return ResourceType::Texture; }
|
||||
template<> inline ResourceType GetResourceType<class Mesh>() { return ResourceType::Mesh; }
|
||||
template<> inline ResourceType GetResourceType<class Material>() { return ResourceType::Material; }
|
||||
template<> inline ResourceType GetResourceType<class Shader>() { return ResourceType::Shader; }
|
||||
template<> inline ResourceType GetResourceType<class AudioClip>() { return ResourceType::AudioClip; }
|
||||
template<> inline ResourceType GetResourceType<class BinaryResource>() { return ResourceType::Binary; }
|
||||
|
||||
class ImportSettings {
|
||||
public:
|
||||
virtual ~ImportSettings() = default;
|
||||
|
||||
virtual Core::UniqueRef<ImportSettings> Clone() const = 0;
|
||||
virtual bool LoadFromJSON(const Containers::String& json) { return false; }
|
||||
virtual Containers::String SaveToJSON() const { return Containers::String(); }
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
24
engine/include/XCEngine/Resources/Resources.h
Normal file
24
engine/include/XCEngine/Resources/Resources.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "ResourceTypes.h"
|
||||
#include "ImportSettings.h"
|
||||
#include "IResource.h"
|
||||
#include "ResourceHandle.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "IResourceLoader.h"
|
||||
#include "ResourceCache.h"
|
||||
#include "AsyncLoader.h"
|
||||
|
||||
// Forward declarations for concrete resource types
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class Texture;
|
||||
class Mesh;
|
||||
class Material;
|
||||
class Shader;
|
||||
class AudioClip;
|
||||
class BinaryResource;
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
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