Skip to content

Commit

Permalink
unify particle buffers, optimize particle removal, allow multithreading
Browse files Browse the repository at this point in the history
  • Loading branch information
RicardoLuis0 committed Sep 23, 2023
1 parent 208f7bd commit b37dc0b
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 80 deletions.
3 changes: 1 addition & 2 deletions src/g_levellocals.h
Original file line number Diff line number Diff line change
Expand Up @@ -657,8 +657,7 @@ struct FLevelLocals

// [RH] particle globals

TArray<particle_t> Particles;
std::deque<particle_t> ReplaceableParticles;
std::deque<particle_t> Particles;

FThinkerCollection Thinkers;

Expand Down
142 changes: 64 additions & 78 deletions src/playsim/p_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
** more useful.
*/

#include <execution>

#include "doomtype.h"
#include "doomstat.h"

Expand All @@ -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");
Expand Down Expand Up @@ -102,33 +106,35 @@ static const struct ColorList {
static_assert(std::is_trivially_copyable_v<particle_t>);

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;
}

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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -232,87 +231,74 @@ 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;
}

void P_ThinkParticles (FLevelLocals *Level)
{
particle_t *particle = nullptr, *prev = nullptr;

TArray<int> 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<particle_t>::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
Expand Down

0 comments on commit b37dc0b

Please sign in to comment.