| 1322 | #if COMPILE_WITH_GPU_PARTICLES |
| 1323 | |
| 1324 | void UpdateGPU(RenderTask* task, GPUContext* context) |
| 1325 | { |
| 1326 | ScopeLock lock(GpuUpdateListLocker); |
| 1327 | if (GpuUpdateList.IsEmpty()) |
| 1328 | return; |
| 1329 | PROFILE_CPU_NAMED("GPUParticles"); |
| 1330 | PROFILE_GPU("GPU Particles"); |
| 1331 | PROFILE_MEM(Particles); |
| 1332 | ScopeReadLock systemScope(Particles::SystemLocker); |
| 1333 | |
| 1334 | // Collect valid emitter tracks to update |
| 1335 | struct GPUSim |
| 1336 | { |
| 1337 | ParticleEffect* Effect; |
| 1338 | ParticleEmitter* Emitter; |
| 1339 | int32 EmitterIndex; |
| 1340 | ParticleEmitterInstance* Data; |
| 1341 | |
| 1342 | bool operator<(const GPUSim& other) const |
| 1343 | { |
| 1344 | // Sort by particle count (larger effects start first) |
| 1345 | if (Data->Buffer->GPU.ParticlesCountMax != other.Data->Buffer->GPU.ParticlesCountMax) |
| 1346 | return Data->Buffer->GPU.ParticlesCountMax > other.Data->Buffer->GPU.ParticlesCountMax; |
| 1347 | if (Emitter->Capacity != other.Emitter->Capacity) |
| 1348 | return Emitter->Capacity > other.Emitter->Capacity; |
| 1349 | |
| 1350 | // Merge emitters together (compute pipeline switches) |
| 1351 | return (uintptr)Emitter < (uintptr)other.Emitter; |
| 1352 | } |
| 1353 | }; |
| 1354 | Array<GPUSim, RendererAllocation> sims; |
| 1355 | sims.EnsureCapacity(Math::AlignUp(GpuUpdateList.Count(), 64)); // Preallocate with some slack |
| 1356 | for (ParticleEffect* effect : GpuUpdateList) |
| 1357 | { |
| 1358 | auto& instance = effect->Instance; |
| 1359 | const auto particleSystem = effect->ParticleSystem.Get(); |
| 1360 | if (!particleSystem || !particleSystem->IsLoaded()) |
| 1361 | continue; |
| 1362 | |
| 1363 | for (int32 j = 0; j < particleSystem->Tracks.Count(); j++) |
| 1364 | { |
| 1365 | const auto& track = particleSystem->Tracks[j]; |
| 1366 | if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) |
| 1367 | continue; |
| 1368 | const int32 emitterIndex = track.AsEmitter.Index; |
| 1369 | ParticleEmitter* emitter = particleSystem->Emitters[emitterIndex].Get(); |
| 1370 | if (!emitter || !emitter->IsLoaded() || emitter->SimulationMode != ParticlesSimulationMode::GPU || instance.Emitters.Count() <= emitterIndex) |
| 1371 | continue; |
| 1372 | ParticleEmitterInstance& data = instance.Emitters[emitterIndex]; |
| 1373 | if (!data.Buffer) |
| 1374 | continue; |
| 1375 | ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0); |
| 1376 | if (!emitter->GPU.CanSim(emitter, data)) |
| 1377 | { |
| 1378 | // Emitters that are culled still might need to clear the particle counter (used for indirect draws) |
| 1379 | if (data.Buffer->GPU.PendingClear) |
| 1380 | emitter->GPU.PreSim(context, emitter, effect, emitterIndex, data); |
| 1381 | continue; |
nothing calls this directly
no test coverage detected