Add deferred async scene asset loading
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -14,8 +15,8 @@ class MeshFilterComponent : public Component {
|
||||
public:
|
||||
std::string GetName() const override { return "MeshFilter"; }
|
||||
|
||||
Resources::Mesh* GetMesh() const { return m_mesh.Get(); }
|
||||
const Resources::ResourceHandle<Resources::Mesh>& GetMeshHandle() const { return m_mesh; }
|
||||
Resources::Mesh* GetMesh() const;
|
||||
const Resources::ResourceHandle<Resources::Mesh>& GetMeshHandle() const;
|
||||
const std::string& GetMeshPath() const { return m_meshPath; }
|
||||
const Resources::AssetRef& GetMeshAssetRef() const { return m_meshRef; }
|
||||
|
||||
@@ -28,9 +29,15 @@ public:
|
||||
void Deserialize(std::istream& is) override;
|
||||
|
||||
private:
|
||||
struct PendingMeshLoadState;
|
||||
|
||||
void BeginAsyncMeshLoad(const std::string& meshPath);
|
||||
void ResolvePendingMesh();
|
||||
|
||||
Resources::ResourceHandle<Resources::Mesh> m_mesh;
|
||||
std::string m_meshPath;
|
||||
Resources::AssetRef m_meshRef;
|
||||
std::shared_ptr<PendingMeshLoadState> m_pendingMeshLoad;
|
||||
};
|
||||
|
||||
} // namespace Components
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -42,12 +43,17 @@ public:
|
||||
void Deserialize(std::istream& is) override;
|
||||
|
||||
private:
|
||||
struct PendingMaterialLoadState;
|
||||
|
||||
void BeginAsyncMaterialLoad(size_t index, const std::string& materialPath);
|
||||
void ResolvePendingMaterials();
|
||||
void EnsureMaterialSlot(size_t index);
|
||||
static std::string MaterialPathFromHandle(const Resources::ResourceHandle<Resources::Material>& material);
|
||||
|
||||
std::vector<Resources::ResourceHandle<Resources::Material>> m_materials;
|
||||
std::vector<std::string> m_materialPaths;
|
||||
std::vector<Resources::AssetRef> m_materialRefs;
|
||||
std::vector<std::shared_ptr<PendingMaterialLoadState>> m_pendingMaterialLoads;
|
||||
bool m_castShadows = true;
|
||||
bool m_receiveShadows = true;
|
||||
uint32_t m_renderLayer = 0;
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include <XCEngine/Core/Asset/ImportSettings.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Threading/Mutex.h>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
@@ -50,6 +53,14 @@ private:
|
||||
static Core::uint64 GenerateRequestId();
|
||||
};
|
||||
|
||||
struct CompletedLoadRequest {
|
||||
LoadRequest request;
|
||||
LoadResult result;
|
||||
|
||||
CompletedLoadRequest(LoadRequest inRequest, LoadResult inResult)
|
||||
: request(std::move(inRequest)), result(std::move(inResult)) {}
|
||||
};
|
||||
|
||||
class AsyncLoader {
|
||||
public:
|
||||
static AsyncLoader& Get();
|
||||
@@ -76,18 +87,21 @@ public:
|
||||
private:
|
||||
void SubmitInternal(LoadRequest request);
|
||||
IResourceLoader* FindLoader(ResourceType type) const;
|
||||
|
||||
void WorkerThread();
|
||||
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::mutex m_queueMutex;
|
||||
std::condition_variable m_pendingCondition;
|
||||
std::deque<LoadRequest> m_pendingQueue;
|
||||
|
||||
std::mutex m_completedMutex;
|
||||
std::deque<CompletedLoadRequest> m_completedQueue;
|
||||
std::vector<std::thread> m_workerThreads;
|
||||
|
||||
std::atomic<bool> m_running{false};
|
||||
std::atomic<Core::uint32> m_pendingCount{0};
|
||||
std::atomic<Core::uint32> m_completedCount{0};
|
||||
Core::uint32 m_totalRequested = 0;
|
||||
std::atomic<Core::uint64> m_totalRequested{0};
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#include <XCEngine/Core/Containers/HashMap.h>
|
||||
#include <XCEngine/Threading/Mutex.h>
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -18,6 +23,17 @@ namespace Resources {
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
class ScopedDeferredSceneLoad {
|
||||
public:
|
||||
explicit ScopedDeferredSceneLoad(ResourceManager& manager = ResourceManager::Get());
|
||||
ScopedDeferredSceneLoad(const ScopedDeferredSceneLoad&) = delete;
|
||||
ScopedDeferredSceneLoad& operator=(const ScopedDeferredSceneLoad&) = delete;
|
||||
~ScopedDeferredSceneLoad();
|
||||
|
||||
private:
|
||||
ResourceManager* m_manager = nullptr;
|
||||
};
|
||||
|
||||
static ResourceManager& Get();
|
||||
|
||||
void Initialize();
|
||||
@@ -43,7 +59,7 @@ public:
|
||||
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
|
||||
|
||||
Containers::String path;
|
||||
if (!assetRef.IsValid() || !m_assetDatabase.TryGetPrimaryAssetPath(assetRef.assetGuid, path)) {
|
||||
if (!TryResolveAssetPath(assetRef, path)) {
|
||||
return ResourceHandle<T>();
|
||||
}
|
||||
|
||||
@@ -54,6 +70,9 @@ public:
|
||||
std::function<void(LoadResult)> callback);
|
||||
void LoadAsync(const Containers::String& path, ResourceType type, ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback);
|
||||
void UpdateAsyncLoads();
|
||||
bool IsAsyncLoading() const;
|
||||
Core::uint32 GetAsyncPendingCount() const;
|
||||
|
||||
void Unload(const Containers::String& path);
|
||||
void Unload(ResourceGUID guid);
|
||||
@@ -98,8 +117,35 @@ public:
|
||||
void UnloadGroup(const Containers::Array<ResourceGUID>& guids);
|
||||
void RefreshAssetDatabase();
|
||||
bool TryGetAssetRef(const Containers::String& path, ResourceType resourceType, AssetRef& outRef) const;
|
||||
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath) const;
|
||||
void BeginDeferredSceneLoad();
|
||||
void EndDeferredSceneLoad();
|
||||
bool IsDeferredSceneLoadEnabled() const;
|
||||
|
||||
private:
|
||||
struct InFlightLoadKey {
|
||||
ResourceGUID guid;
|
||||
ResourceType type = ResourceType::Unknown;
|
||||
|
||||
bool operator==(const InFlightLoadKey& other) const {
|
||||
return guid == other.guid && type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
struct InFlightLoadKeyHasher {
|
||||
size_t operator()(const InFlightLoadKey& key) const noexcept {
|
||||
return std::hash<ResourceGUID>{}(key.guid) ^
|
||||
(static_cast<size_t>(key.type) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct InFlightLoadState {
|
||||
bool completed = false;
|
||||
bool success = false;
|
||||
Containers::String errorMessage;
|
||||
std::condition_variable condition;
|
||||
};
|
||||
|
||||
ResourceManager() = default;
|
||||
~ResourceManager() = default;
|
||||
|
||||
@@ -122,8 +168,13 @@ private:
|
||||
ResourceCache m_cache;
|
||||
Core::UniqueRef<AsyncLoader> m_asyncLoader;
|
||||
Threading::Mutex m_mutex;
|
||||
mutable std::recursive_mutex m_ioMutex;
|
||||
std::mutex m_inFlightLoadsMutex;
|
||||
std::unordered_map<InFlightLoadKey, std::shared_ptr<InFlightLoadState>, InFlightLoadKeyHasher> m_inFlightLoads;
|
||||
std::atomic<Core::uint32> m_deferredSceneLoadDepth{0};
|
||||
|
||||
friend class ResourceHandleBase;
|
||||
friend class AsyncLoader;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
|
||||
Reference in New Issue
Block a user