Add deferred async scene asset loading
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
#include "Components/MeshRendererComponent.h"
|
||||
|
||||
#include "Components/GameObject.h"
|
||||
#include "Core/Asset/ResourceManager.h"
|
||||
#include "Debug/Logger.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -13,6 +15,12 @@ std::string ToStdString(const Containers::String& value) {
|
||||
return std::string(value.CStr());
|
||||
}
|
||||
|
||||
bool ShouldTraceMaterialPath(const std::string& path) {
|
||||
return path.rfind("builtin://", 0) == 0 ||
|
||||
path.find("backpack") != std::string::npos ||
|
||||
path.find("New Material.mat") != std::string::npos;
|
||||
}
|
||||
|
||||
std::string EncodeAssetRef(const Resources::AssetRef& assetRef) {
|
||||
if (!assetRef.IsValid()) {
|
||||
return std::string();
|
||||
@@ -83,13 +91,66 @@ std::vector<Resources::AssetRef> SplitMaterialRefs(const std::string& value) {
|
||||
return refs;
|
||||
}
|
||||
|
||||
std::string JoinMaterialPaths(const std::vector<std::string>& values) {
|
||||
std::ostringstream stream;
|
||||
for (size_t index = 0; index < values.size(); ++index) {
|
||||
if (index > 0) {
|
||||
stream << "|";
|
||||
}
|
||||
stream << values[index];
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string JoinMaterialRefs(const std::vector<Resources::AssetRef>& values) {
|
||||
std::ostringstream stream;
|
||||
for (size_t index = 0; index < values.size(); ++index) {
|
||||
if (index > 0) {
|
||||
stream << "|";
|
||||
}
|
||||
stream << EncodeAssetRef(values[index]);
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
bool ShouldTraceAnyMaterialPath(const std::vector<std::string>& values) {
|
||||
for (const std::string& value : values) {
|
||||
if (ShouldTraceMaterialPath(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DescribeOwner(const MeshRendererComponent& component) {
|
||||
const GameObject* owner = component.GetGameObject();
|
||||
if (owner == nullptr) {
|
||||
return "<no-owner>";
|
||||
}
|
||||
|
||||
return std::string("id=") + std::to_string(owner->GetID()) + ",name=" + owner->GetName();
|
||||
}
|
||||
|
||||
void TraceMeshRenderer(const MeshRendererComponent& component, const std::string& message) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String(("[MeshRenderer] owner={" + DescribeOwner(component) + "} " + message).c_str()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct MeshRendererComponent::PendingMaterialLoadState {
|
||||
Resources::LoadResult result;
|
||||
bool completed = false;
|
||||
};
|
||||
|
||||
Resources::Material* MeshRendererComponent::GetMaterial(size_t index) const {
|
||||
const_cast<MeshRendererComponent*>(this)->ResolvePendingMaterials();
|
||||
return index < m_materials.size() ? m_materials[index].Get() : nullptr;
|
||||
}
|
||||
|
||||
const Resources::ResourceHandle<Resources::Material>& MeshRendererComponent::GetMaterialHandle(size_t index) const {
|
||||
const_cast<MeshRendererComponent*>(this)->ResolvePendingMaterials();
|
||||
static const Resources::ResourceHandle<Resources::Material> kNullHandle;
|
||||
return index < m_materials.size() ? m_materials[index] : kNullHandle;
|
||||
}
|
||||
@@ -101,6 +162,7 @@ const std::string& MeshRendererComponent::GetMaterialPath(size_t index) const {
|
||||
|
||||
void MeshRendererComponent::SetMaterialPath(size_t index, const std::string& materialPath) {
|
||||
EnsureMaterialSlot(index);
|
||||
m_pendingMaterialLoads[index].reset();
|
||||
m_materialPaths[index] = materialPath;
|
||||
if (materialPath.empty()) {
|
||||
m_materials[index].Reset();
|
||||
@@ -112,10 +174,20 @@ void MeshRendererComponent::SetMaterialPath(size_t index, const std::string& mat
|
||||
if (!Resources::ResourceManager::Get().TryGetAssetRef(materialPath.c_str(), Resources::ResourceType::Material, m_materialRefs[index])) {
|
||||
m_materialRefs[index].Reset();
|
||||
}
|
||||
|
||||
if (ShouldTraceMaterialPath(materialPath)) {
|
||||
TraceMeshRenderer(
|
||||
*this,
|
||||
std::string("SetMaterialPath slot=") + std::to_string(index) +
|
||||
" path=" + materialPath +
|
||||
" ref=" + EncodeAssetRef(m_materialRefs[index]) +
|
||||
" loaded=" + (m_materials[index].Get() != nullptr ? "1" : "0"));
|
||||
}
|
||||
}
|
||||
|
||||
void MeshRendererComponent::SetMaterial(size_t index, const Resources::ResourceHandle<Resources::Material>& material) {
|
||||
EnsureMaterialSlot(index);
|
||||
m_pendingMaterialLoads[index].reset();
|
||||
m_materials[index] = material;
|
||||
m_materialPaths[index] = MaterialPathFromHandle(material);
|
||||
if (m_materialPaths[index].empty() ||
|
||||
@@ -132,6 +204,8 @@ void MeshRendererComponent::SetMaterials(const std::vector<Resources::ResourceHa
|
||||
m_materials = materials;
|
||||
m_materialPaths.resize(materials.size());
|
||||
m_materialRefs.resize(materials.size());
|
||||
m_pendingMaterialLoads.clear();
|
||||
m_pendingMaterialLoads.resize(materials.size());
|
||||
for (size_t i = 0; i < materials.size(); ++i) {
|
||||
m_materialPaths[i] = MaterialPathFromHandle(materials[i]);
|
||||
if (m_materialPaths[i].empty() ||
|
||||
@@ -145,6 +219,7 @@ void MeshRendererComponent::ClearMaterials() {
|
||||
m_materials.clear();
|
||||
m_materialPaths.clear();
|
||||
m_materialRefs.clear();
|
||||
m_pendingMaterialLoads.clear();
|
||||
}
|
||||
|
||||
void MeshRendererComponent::Serialize(std::ostream& os) const {
|
||||
@@ -194,6 +269,7 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
|
||||
m_materialPaths = SplitMaterialPaths(value);
|
||||
m_materials.resize(m_materialPaths.size());
|
||||
m_materialRefs.resize(m_materialPaths.size());
|
||||
m_pendingMaterialLoads.resize(m_materialPaths.size());
|
||||
} else if (key == "materialRefs") {
|
||||
pendingMaterialRefs = SplitMaterialRefs(value);
|
||||
} else if (key == "castShadows") {
|
||||
@@ -209,28 +285,149 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
|
||||
m_materialPaths.resize(pendingMaterialRefs.size());
|
||||
m_materials.resize(pendingMaterialRefs.size());
|
||||
m_materialRefs.resize(pendingMaterialRefs.size());
|
||||
m_pendingMaterialLoads.resize(pendingMaterialRefs.size());
|
||||
}
|
||||
|
||||
if (ShouldTraceAnyMaterialPath(m_materialPaths)) {
|
||||
TraceMeshRenderer(
|
||||
*this,
|
||||
std::string("Deserialize paths=") + JoinMaterialPaths(m_materialPaths) +
|
||||
" refs=" + JoinMaterialRefs(pendingMaterialRefs) +
|
||||
" deferred=" + (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled() ? "1" : "0"));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_materialPaths.size(); ++i) {
|
||||
bool restoredOrQueued = false;
|
||||
if (i < pendingMaterialRefs.size() && pendingMaterialRefs[i].IsValid()) {
|
||||
m_materialRefs[i] = pendingMaterialRefs[i];
|
||||
m_materials[i] = Resources::ResourceManager::Get().Load<Resources::Material>(pendingMaterialRefs[i]);
|
||||
if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) {
|
||||
Containers::String resolvedPath;
|
||||
if (Resources::ResourceManager::Get().TryResolveAssetPath(pendingMaterialRefs[i], resolvedPath)) {
|
||||
m_materialPaths[i] = ToStdString(resolvedPath);
|
||||
if (ShouldTraceMaterialPath(m_materialPaths[i])) {
|
||||
TraceMeshRenderer(
|
||||
*this,
|
||||
std::string("Resolved materialRef slot=") + std::to_string(i) +
|
||||
" path=" + m_materialPaths[i]);
|
||||
}
|
||||
BeginAsyncMaterialLoad(i, m_materialPaths[i]);
|
||||
restoredOrQueued = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!restoredOrQueued) {
|
||||
m_materials[i] = Resources::ResourceManager::Get().Load<Resources::Material>(pendingMaterialRefs[i]);
|
||||
}
|
||||
if (m_materials[i].Get() != nullptr) {
|
||||
m_materialPaths[i] = MaterialPathFromHandle(m_materials[i]);
|
||||
restoredOrQueued = true;
|
||||
}
|
||||
|
||||
if (restoredOrQueued) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) {
|
||||
if (!m_materialPaths[i].empty()) {
|
||||
if (!m_materialRefs[i].IsValid() &&
|
||||
!Resources::ResourceManager::Get().TryGetAssetRef(m_materialPaths[i].c_str(),
|
||||
Resources::ResourceType::Material,
|
||||
m_materialRefs[i])) {
|
||||
m_materialRefs[i].Reset();
|
||||
}
|
||||
BeginAsyncMaterialLoad(i, m_materialPaths[i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
SetMaterialPath(i, m_materialPaths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshRendererComponent::BeginAsyncMaterialLoad(size_t index, const std::string& materialPath) {
|
||||
EnsureMaterialSlot(index);
|
||||
if (materialPath.empty()) {
|
||||
m_pendingMaterialLoads[index].reset();
|
||||
m_materials[index].Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_materials[index].Reset();
|
||||
m_pendingMaterialLoads[index] = std::make_shared<PendingMaterialLoadState>();
|
||||
if (ShouldTraceMaterialPath(materialPath)) {
|
||||
TraceMeshRenderer(
|
||||
*this,
|
||||
std::string("BeginAsyncMaterialLoad slot=") + std::to_string(index) +
|
||||
" path=" + materialPath);
|
||||
}
|
||||
std::weak_ptr<PendingMaterialLoadState> weakState = m_pendingMaterialLoads[index];
|
||||
Resources::ResourceManager::Get().LoadAsync(materialPath.c_str(),
|
||||
Resources::ResourceType::Material,
|
||||
[weakState](Resources::LoadResult result) {
|
||||
if (std::shared_ptr<PendingMaterialLoadState> state = weakState.lock()) {
|
||||
state->result = std::move(result);
|
||||
state->completed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MeshRendererComponent::ResolvePendingMaterials() {
|
||||
for (size_t index = 0; index < m_pendingMaterialLoads.size(); ++index) {
|
||||
if (!m_pendingMaterialLoads[index] || !m_pendingMaterialLoads[index]->completed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<PendingMaterialLoadState> completedLoad = std::move(m_pendingMaterialLoads[index]);
|
||||
m_pendingMaterialLoads[index].reset();
|
||||
|
||||
if (!completedLoad->result || completedLoad->result.resource == nullptr) {
|
||||
if (ShouldTraceMaterialPath(m_materialPaths[index])) {
|
||||
TraceMeshRenderer(
|
||||
*this,
|
||||
std::string("ResolvePendingMaterial failed slot=") + std::to_string(index) +
|
||||
" path=" + m_materialPaths[index] +
|
||||
" error=" + ToStdString(completedLoad->result.errorMessage));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m_materials[index] = Resources::ResourceHandle<Resources::Material>(
|
||||
static_cast<Resources::Material*>(completedLoad->result.resource));
|
||||
if (m_materials[index].Get() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_materialPaths[index] = MaterialPathFromHandle(m_materials[index]);
|
||||
if (!Resources::ResourceManager::Get().TryGetAssetRef(m_materialPaths[index].c_str(),
|
||||
Resources::ResourceType::Material,
|
||||
m_materialRefs[index])) {
|
||||
m_materialRefs[index].Reset();
|
||||
}
|
||||
|
||||
if (ShouldTraceMaterialPath(m_materialPaths[index])) {
|
||||
TraceMeshRenderer(
|
||||
*this,
|
||||
std::string("ResolvePendingMaterial success slot=") + std::to_string(index) +
|
||||
" path=" + m_materialPaths[index] +
|
||||
" ref=" + EncodeAssetRef(m_materialRefs[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MeshRendererComponent::EnsureMaterialSlot(size_t index) {
|
||||
if (index >= m_materials.size()) {
|
||||
m_materials.resize(index + 1);
|
||||
}
|
||||
if (index >= m_materialPaths.size()) {
|
||||
m_materialPaths.resize(index + 1);
|
||||
}
|
||||
if (index >= m_materialRefs.size()) {
|
||||
m_materialRefs.resize(index + 1);
|
||||
}
|
||||
if (index >= m_pendingMaterialLoads.size()) {
|
||||
m_pendingMaterialLoads.resize(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string MeshRendererComponent::MaterialPathFromHandle(const Resources::ResourceHandle<Resources::Material>& material) {
|
||||
|
||||
Reference in New Issue
Block a user