diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index 6264ce412..558c0172f 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -9,7 +9,9 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Types/DataContainer.h" #include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h" +#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Level/Level.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Threading/Threading.h" @@ -58,6 +60,35 @@ ParticleEffect* ParticleEmitter::Spawn(Actor* parent, const Transform& transform return effect; } +void ParticleEmitter::WaitForAsset(Asset* asset) +{ + // TODO: make a tool for it? + if (auto* texture = Cast(asset)) + { + // Wait for asset to be loaded + if (!texture->WaitForLoaded() && !IsInMainThread() && texture->GetTexture()) + { + PROFILE_CPU_NAMED("WaitForTexture"); + + // Mark as used by rendering and wait for streaming to load it in + double waitTimeSeconds = 10000; + double now = Platform::GetTimeSeconds(); + double end = now + waitTimeSeconds; + const int32 mipLevels = texture->GetMipLevels(); + constexpr float requestedResidency = 0.7f; + const int32 minMipLevels = Math::Max(Math::Min(mipLevels, 7), (int32)(requestedResidency * (float)mipLevels)); + while (texture->GetResidentMipLevels() < minMipLevels && + now < end && + !texture->HasStreamingError()) + { + texture->GetTexture()->LastRenderTime = now; + Platform::Sleep(1); + now = Platform::GetTimeSeconds(); + } + } + } +} + #if COMPILE_WITH_PARTICLE_GPU_GRAPH && COMPILE_WITH_SHADER_COMPILER namespace @@ -294,6 +325,40 @@ Asset::LoadResult ParticleEmitter::load() SimulationMode = ParticlesSimulationMode::CPU; #endif + // Wait for resources used by the emitter to be loaded + // eg. texture used to place particles on spawn needs to be available + bool waitForAsset = false; + for (const auto& node : Graph.Nodes) + { + if ((node.Type == GRAPH_NODE_MAKE_TYPE(5, 1) || node.Type == GRAPH_NODE_MAKE_TYPE(5, 2)) && node.Assets.Count() > 0) + { + const auto texture = node.Assets[0].As(); + if (texture) + { + if (!waitForAsset) + { + waitForAsset = true; + Particles::SystemLocker.End(true); + } + WaitForAsset(texture); + } + } + } + for (const auto& parameter : Graph.Parameters) + { + if (parameter.Type.Type == VariantType::Asset) + { + if (!waitForAsset) + { + waitForAsset = true; + Particles::SystemLocker.End(true); + } + WaitForAsset((Asset*)parameter.Value); + } + } + if (waitForAsset) + Particles::SystemLocker.Begin(true); + return LoadResult::Ok; } diff --git a/Source/Engine/Particles/ParticleEmitter.h b/Source/Engine/Particles/ParticleEmitter.h index 772f5569e..1398de5db 100644 --- a/Source/Engine/Particles/ParticleEmitter.h +++ b/Source/Engine/Particles/ParticleEmitter.h @@ -168,6 +168,9 @@ public: /// The spawned effect. API_FUNCTION() ParticleEffect* Spawn(Actor* parent, const Transform& transform, float duration = MAX_float, bool autoDestroy = false); +private: + void WaitForAsset(Asset* asset); + public: // [BinaryAsset] #if USE_EDITOR