From eea236fe8ddcbea6961b45813fa9924a9be2e883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Sat, 23 Sep 2023 15:41:35 -0300 Subject: [PATCH] unify particle buffers, optimize particle removal --- src/g_levellocals.h | 3 +- src/playsim/p_effect.cpp | 142 ++++++++++++++++++--------------------- 2 files changed, 65 insertions(+), 80 deletions(-) diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 727d6a4904d..cf3e10a1c1f 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -657,8 +657,7 @@ struct FLevelLocals // [RH] particle globals - TArray Particles; - std::deque ReplaceableParticles; + std::deque Particles; FThinkerCollection Thinkers; diff --git a/src/playsim/p_effect.cpp b/src/playsim/p_effect.cpp index 77c8866a2b6..0fd25f12389 100644 --- a/src/playsim/p_effect.cpp +++ b/src/playsim/p_effect.cpp @@ -34,6 +34,8 @@ ** more useful. */ +#include + #include "doomtype.h" #include "doomstat.h" @@ -49,12 +51,14 @@ #include "vm.h" #include "actorinlines.h" #include "g_game.h" +#include "i_time.h" CVAR (Int, cl_rockettrails, 1, CVAR_ARCHIVE); CVAR (Bool, r_rail_smartspiral, false, CVAR_ARCHIVE); CVAR (Int, r_rail_spiralsparsity, 1, CVAR_ARCHIVE); CVAR (Int, r_rail_trailsparsity, 1, CVAR_ARCHIVE); CVAR (Bool, r_particles, true, 0); +CVAR (Bool, r_particles_multithreaded, true, CVAR_ARCHIVE); EXTERN_CVAR(Int, r_maxparticles); FRandom pr_railtrail("RailTrail"); @@ -102,11 +106,13 @@ static const struct ColorList { static_assert(std::is_trivially_copyable_v); static int ParticleCount; +static float ParticleThinkMs; +static uint64_t ParticleCreateNs; ADD_STAT(particles) { FString str; - str.Format("Particle Count: %d", ParticleCount); + str.Format("Particle Count: %d, Particle Think Time: %.2f ms, Particle Create Time: %.2f ms", ParticleCount, ParticleThinkMs, (ParticleCreateNs / 1000000.0f)); return str; } @@ -114,21 +120,21 @@ static int NumParticles; inline particle_t *NewParticle (FLevelLocals *Level, bool replace = false) { - int num_parts = Level->Particles.Size() + (int)Level->ReplaceableParticles.size(); - if(replace) + if(NumParticles > 0) { - if(num_parts == NumParticles) + uint64_t start = I_nsTime(); + if(ParticleCount == NumParticles) { - if(Level->ReplaceableParticles.size() == 0) return nullptr; - Level->ReplaceableParticles.pop_front(); + if(!replace) return nullptr; + Level->Particles.pop_front(); } - Level->ReplaceableParticles.push_back({}); - return &Level->ReplaceableParticles[Level->ReplaceableParticles.size() - 1]; - } - else if(num_parts < NumParticles) - { - Level->Particles.Reserve(1); - return Level->Particles.Data() + (Level->Particles.Size() - 1); + else + { + ParticleCount++; + } + Level->Particles.push_back({}); + ParticleCreateNs += (I_nsTime() - start); + return &*Level->Particles.rbegin(); } return nullptr; } @@ -157,8 +163,7 @@ void P_InitParticles (FLevelLocals *Level) void P_ClearParticles (FLevelLocals *Level) { - Level->Particles.Clear(); - Level->ReplaceableParticles.clear(); + Level->Particles.clear(); } // Group particles by subsectors. Because particles are always @@ -176,21 +181,15 @@ void P_FindParticleSubsectors (FLevelLocals *Level) { return; } - int n = Level->Particles.Size(); - for (int i = 0; i < n; i++) - { - particle_t * part = Level->Particles.Data() + i; - // Try to reuse the subsector from the last portal check, if still valid. - if (part->subsector == nullptr) part->subsector = Level->PointInRenderSubsector(part->Pos); - part->subsector->particles.Push(part); - } - n = Level->ReplaceableParticles.size(); - for (int i = 0; i < n; i++) + + auto end = Level->Particles.end(); + + for(auto it = Level->Particles.begin(); it != end; it++) { - particle_t * part = &Level->ReplaceableParticles[i]; + particle_t * p = &*it; // Try to reuse the subsector from the last portal check, if still valid. - if (Level->ReplaceableParticles[i].subsector == nullptr) Level->ReplaceableParticles[i].subsector = Level->PointInRenderSubsector(part->Pos); - part->subsector->particles.Push(part); + if (p->subsector == nullptr) p->subsector = Level->PointInRenderSubsector(p->Pos); + p->subsector->particles.Push(p); } } @@ -232,46 +231,46 @@ void P_InitEffects () } // [RL0] true = particle should be deleted -bool P_ThinkParticle(FLevelLocals * Level, particle_t * particle) +bool P_ThinkParticle(FLevelLocals * Level, particle_t &particle) { - auto oldtrans = particle->alpha; - particle->alpha -= particle->fadestep; - particle->size += particle->sizestep; - if (particle->alpha <= 0 || oldtrans < particle->alpha || --particle->ttl <= 0 || (particle->size <= 0)) + auto oldtrans = particle.alpha; + particle.alpha -= particle.fadestep; + particle.size += particle.sizestep; + if(particle.alpha <= 0 || oldtrans < particle.alpha || --particle.ttl <= 0 || (particle.size <= 0)) { // The particle has expired, so free it return true; } - DVector2 newxy = Level->GetPortalOffsetPosition(particle->Pos.X, particle->Pos.Y, particle->Vel.X, particle->Vel.Y); - particle->Pos.X = newxy.X; - particle->Pos.Y = newxy.Y; - particle->Pos.Z += particle->Vel.Z; - particle->Vel += particle->Acc; + DVector2 newxy = Level->GetPortalOffsetPosition(particle.Pos.X, particle.Pos.Y, particle.Vel.X, particle.Vel.Y); + particle.Pos.X = newxy.X; + particle.Pos.Y = newxy.Y; + particle.Pos.Z += particle.Vel.Z; + particle.Vel += particle.Acc; - if(particle->flags & PT_DOROLL) + if(particle.flags & PT_DOROLL) { - particle->Roll += particle->RollVel; - particle->RollVel += particle->RollAcc; + particle.Roll += particle.RollVel; + particle.RollVel += particle.RollAcc; } - particle->subsector = Level->PointInRenderSubsector(particle->Pos); - sector_t *s = particle->subsector->sector; + particle.subsector = Level->PointInRenderSubsector(particle.Pos); + sector_t *s = particle.subsector->sector; // Handle crossing a sector portal. if (!s->PortalBlocksMovement(sector_t::ceiling)) { - if (particle->Pos.Z > s->GetPortalPlaneZ(sector_t::ceiling)) + if (particle.Pos.Z > s->GetPortalPlaneZ(sector_t::ceiling)) { - particle->Pos += s->GetPortalDisplacement(sector_t::ceiling); - particle->subsector = NULL; + particle.Pos += s->GetPortalDisplacement(sector_t::ceiling); + particle.subsector = NULL; } } else if (!s->PortalBlocksMovement(sector_t::floor)) { - if (particle->Pos.Z < s->GetPortalPlaneZ(sector_t::floor)) + if (particle.Pos.Z < s->GetPortalPlaneZ(sector_t::floor)) { - particle->Pos += s->GetPortalDisplacement(sector_t::floor); - particle->subsector = NULL; + particle.Pos += s->GetPortalDisplacement(sector_t::floor); + particle.subsector = NULL; } } return false; @@ -279,40 +278,27 @@ bool P_ThinkParticle(FLevelLocals * Level, particle_t * particle) void P_ThinkParticles (FLevelLocals *Level) { - particle_t *particle = nullptr, *prev = nullptr; - - TArray expired, expiredReplaceable; - - int n = Level->Particles.Size(); - - int last_alive = Level->Particles.Size() - 1; - - // iterate backwards to allow simple removal from the array - for(int i = last_alive; i > 0; i--) + uint64_t startNs = I_nsTime(); + std::deque::iterator newEnd; + if(r_particles_multithreaded) { - particle_t * p = Level->Particles.Data() + i; - if(P_ThinkParticle(Level, p)) + newEnd = std::remove_if(std::execution::par_unseq, Level->Particles.begin(), Level->Particles.end(), [Level](particle_t &p) { - if(i != last_alive) memcpy(p, Level->Particles.Data() + last_alive, sizeof(particle_t)); - last_alive--; - } + return P_ThinkParticle(Level, p); + }); } - Level->Particles.Resize(last_alive + 1); - - last_alive = ((int)Level->ReplaceableParticles.size()) - 1; - for(int i = last_alive; i > 0; i--) + else { - particle_t * p = &Level->ReplaceableParticles[i]; - if(P_ThinkParticle(Level, p)) + newEnd = std::remove_if(Level->Particles.begin(), Level->Particles.end(), [Level](particle_t &p) { - if(i != last_alive) memcpy(p, &Level->ReplaceableParticles[last_alive], sizeof(particle_t)); - last_alive--; - } - } - - Level->ReplaceableParticles.resize(last_alive + 1); - - ParticleCount = Level->Particles.Size() + (int)Level->ReplaceableParticles.size(); + return P_ThinkParticle(Level, p); + }); + } + + Level->Particles.resize(newEnd - Level->Particles.begin()); + ParticleCount = Level->Particles.size(); + ParticleThinkMs = ( I_nsTime() - startNs) / 1000000.0f; + ParticleCreateNs = 0; } enum PSFlag