Fix crash in particles system when assets gets loading/unloaded while async jobs are active

This commit is contained in:
Wojtek Figat
2025-02-18 23:27:49 +01:00
parent 060bc0aaf8
commit 937d369856
5 changed files with 26 additions and 3 deletions

View File

@@ -120,7 +120,7 @@ void AnimationsSystem::Execute(TaskGraph* graph)
return; return;
Active = true; Active = true;
// Ensure no animation assets it being reloading/modified before running async update // Ensure no animation assets can be reloaded/modified during async update
Animations::SystemLocker.Begin(false); Animations::SystemLocker.Begin(false);
// Setup data for async update // Setup data for async update

View File

@@ -72,6 +72,8 @@ namespace
Asset::LoadResult ParticleEmitter::load() Asset::LoadResult ParticleEmitter::load()
{ {
ConcurrentSystemLocker::WriteScope systemScope(Particles::SystemLocker);
// Load the graph // Load the graph
const auto surfaceChunk = GetChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE); const auto surfaceChunk = GetChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE);
if (!surfaceChunk) if (!surfaceChunk)
@@ -287,6 +289,7 @@ Asset::LoadResult ParticleEmitter::load()
void ParticleEmitter::unload(bool isReloading) void ParticleEmitter::unload(bool isReloading)
{ {
ConcurrentSystemLocker::WriteScope systemScope(Particles::SystemLocker);
#if COMPILE_WITH_SHADER_COMPILER #if COMPILE_WITH_SHADER_COMPILER
UnregisterForShaderReloads(this); UnregisterForShaderReloads(this);
#endif #endif
@@ -389,7 +392,7 @@ bool ParticleEmitter::SaveSurface(BytesContainer& data)
LOG(Error, "Asset loading failed. Cannot save it."); LOG(Error, "Asset loading failed. Cannot save it.");
return true; return true;
} }
ConcurrentSystemLocker::WriteScope systemScope(Particles::SystemLocker);
ScopeLock lock(Locker); ScopeLock lock(Locker);
// Release all chunks // Release all chunks

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "ParticleEmitterFunction.h" #include "ParticleEmitterFunction.h"
#include "Particles.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
@@ -39,6 +40,8 @@ ParticleEmitterFunction::ParticleEmitterFunction(const SpawnParams& params, cons
Asset::LoadResult ParticleEmitterFunction::load() Asset::LoadResult ParticleEmitterFunction::load()
{ {
ConcurrentSystemLocker::WriteScope systemScope(Particles::SystemLocker);
// Load graph // Load graph
const auto surfaceChunk = GetChunk(0); const auto surfaceChunk = GetChunk(0);
if (!surfaceChunk || !surfaceChunk->IsLoaded()) if (!surfaceChunk || !surfaceChunk->IsLoaded())
@@ -90,6 +93,7 @@ Asset::LoadResult ParticleEmitterFunction::load()
void ParticleEmitterFunction::unload(bool isReloading) void ParticleEmitterFunction::unload(bool isReloading)
{ {
ConcurrentSystemLocker::WriteScope systemScope(Particles::SystemLocker);
Graph.Clear(); Graph.Clear();
#if COMPILE_WITH_PARTICLE_GPU_GRAPH #if COMPILE_WITH_PARTICLE_GPU_GRAPH
GraphGPU.Clear(); GraphGPU.Clear();
@@ -190,7 +194,7 @@ bool ParticleEmitterFunction::SaveSurface(BytesContainer& data)
LOG(Error, "Asset loading failed. Cannot save it."); LOG(Error, "Asset loading failed. Cannot save it.");
return true; return true;
} }
ConcurrentSystemLocker::WriteScope systemScope(Particles::SystemLocker);
ScopeLock lock(Locker); ScopeLock lock(Locker);
// Set Visject Surface data // Set Visject Surface data

View File

@@ -111,6 +111,7 @@ namespace ParticleManagerImpl
using namespace ParticleManagerImpl; using namespace ParticleManagerImpl;
TaskGraphSystem* Particles::System = nullptr; TaskGraphSystem* Particles::System = nullptr;
ConcurrentSystemLocker Particles::SystemLocker;
bool Particles::EnableParticleBufferPooling = true; bool Particles::EnableParticleBufferPooling = true;
float Particles::ParticleBufferRecycleTimeout = 10.0f; float Particles::ParticleBufferRecycleTimeout = 10.0f;
@@ -139,6 +140,8 @@ class ParticlesSystem : public TaskGraphSystem
{ {
public: public:
float DeltaTime, UnscaledDeltaTime, Time, UnscaledTime; float DeltaTime, UnscaledDeltaTime, Time, UnscaledTime;
bool Active;
void Job(int32 index); void Job(int32 index);
void Execute(TaskGraph* graph) override; void Execute(TaskGraph* graph) override;
void PostExecute(TaskGraph* graph) override; void PostExecute(TaskGraph* graph) override;
@@ -1390,6 +1393,10 @@ void ParticlesSystem::Execute(TaskGraph* graph)
{ {
if (UpdateList.Count() == 0) if (UpdateList.Count() == 0)
return; return;
Active = true;
// Ensure no particle assets can be reloaded/modified during async update
Particles::SystemLocker.Begin(false);
// Setup data for async update // Setup data for async update
const auto& tickData = Time::Update; const auto& tickData = Time::Update;
@@ -1406,8 +1413,13 @@ void ParticlesSystem::Execute(TaskGraph* graph)
void ParticlesSystem::PostExecute(TaskGraph* graph) void ParticlesSystem::PostExecute(TaskGraph* graph)
{ {
if (!Active)
return;
PROFILE_CPU_NAMED("Particles.PostExecute"); PROFILE_CPU_NAMED("Particles.PostExecute");
// Cleanup
Particles::SystemLocker.End(false);
Active = false;
UpdateList.Clear(); UpdateList.Clear();
#if COMPILE_WITH_GPU_PARTICLES #if COMPILE_WITH_GPU_PARTICLES

View File

@@ -3,6 +3,7 @@
#pragma once #pragma once
#include "Engine/Scripting/ScriptingType.h" #include "Engine/Scripting/ScriptingType.h"
#include "Engine/Threading/ConcurrentSystemLocker.h"
class TaskGraphSystem; class TaskGraphSystem;
struct RenderContext; struct RenderContext;
@@ -27,6 +28,9 @@ API_CLASS(Static) class FLAXENGINE_API Particles
/// </summary> /// </summary>
API_FIELD(ReadOnly) static TaskGraphSystem* System; API_FIELD(ReadOnly) static TaskGraphSystem* System;
// Data access locker for animations data.
static ConcurrentSystemLocker SystemLocker;
public: public:
/// <summary> /// <summary>
/// Updates the effect during next particles simulation tick. /// Updates the effect during next particles simulation tick.