Stop calling P_NullPrecipThinker

In levels with tens of thousands of precipmobjs, the
overhead of running a thinker for all of them is too much,
no matter how small the thinker is.

ALL thinking is done inside the renderer now, where it can
be limited by distance. Precipmobjs track the last
leveltime they thunk, so interpolated frames don't think
twice.
This commit is contained in:
James R 2023-04-04 04:29:47 -07:00
parent ef382feba9
commit 9eab5317f3
8 changed files with 83 additions and 51 deletions

View file

@ -5653,6 +5653,12 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
// uncapped/interpolation
interpmobjstate_t interp = {0};
// okay... this is a hack, but weather isn't networked, so it should be ok
if (!P_PrecipThinker(thing))
{
return;
}
// do interpolation
if (R_UsingFrameInterpolation() && !paused)
{
@ -5752,13 +5758,6 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
vis->precip = true;
vis->bbox = false;
// okay... this is a hack, but weather isn't networked, so it should be ok
if (!(thing->precipflags & PCF_THUNK))
{
P_PrecipThinker(thing);
thing->precipflags |= PCF_THUNK;
}
}
#endif

View file

@ -51,7 +51,7 @@ precise_t ps_playerthink_time = 0;
precise_t ps_botticcmd_time = 0;
precise_t ps_thinkertime = 0;
precise_t ps_thlist_times[NUM_THINKERLISTS];
precise_t ps_thlist_times[NUM_ACTIVETHINKERLISTS];
precise_t ps_acs_time = 0;
int ps_checkposition_calls = 0;
@ -367,7 +367,6 @@ static void M_DrawTickStats(void)
{"main ", "Main: ", &ps_thlist_times[THINK_MAIN]},
{"mobjs ", "Mobjs: ", &ps_thlist_times[THINK_MOBJ]},
{"dynslop", "Dynamic slopes: ", &ps_thlist_times[THINK_DYNSLOPE]},
{"precip ", "Precipitation: ", &ps_thlist_times[THINK_PRECIP]},
{0}
};

View file

@ -72,7 +72,13 @@ typedef enum
THINK_MAIN,
THINK_MOBJ,
THINK_DYNSLOPE,
THINK_PRECIP,
// Lists after this may exist but they do not call an
// action in P_RunThinkers
NUM_ACTIVETHINKERLISTS,
THINK_PRECIP = NUM_ACTIVETHINKERLISTS,
NUM_THINKERLISTS
} thinklistnum_t; /**< Thinker lists. */
extern thinker_t thlist[];
@ -80,6 +86,7 @@ extern thinker_t thlist[];
void P_InitThinkers(void);
void P_AddThinker(const thinklistnum_t n, thinker_t *thinker);
void P_RemoveThinker(thinker_t *thinker);
void P_UnlinkThinker(thinker_t *thinker);
//
// P_USER

View file

@ -546,7 +546,7 @@ static boolean P_SetPrecipMobjState(precipmobj_t *mobj, statenum_t state)
if (state == S_NULL)
{ // Remove mobj
P_RemovePrecipMobj(mobj);
P_FreePrecipMobj(mobj);
return false;
}
st = &states[state];
@ -4174,25 +4174,33 @@ void P_RecalcPrecipInSector(sector_t *sector)
//
// P_NullPrecipThinker
//
// For "Blank" precipitation
// Just the identification of a precip thinker. The thinker
// should never actually be called!
//
void P_NullPrecipThinker(precipmobj_t *mobj)
{
//(void)mobj;
mobj->precipflags &= ~PCF_THUNK;
R_ResetPrecipitationMobjInterpolationState(mobj);
(void)mobj;
I_Assert("P_NullPrecipThinker should not be called" == 0);
}
void P_PrecipThinker(precipmobj_t *mobj)
boolean P_PrecipThinker(precipmobj_t *mobj)
{
boolean flip = (mobj->precipflags & PCF_FLIP);
if (mobj->lastThink == leveltime)
return true; // already thinked this tick
mobj->lastThink = leveltime;
R_ResetPrecipitationMobjInterpolationState(mobj);
P_CycleStateAnimation((mobj_t *)mobj);
if (mobj->state == &states[S_RAINRETURN])
{
// Reset to ceiling!
P_SetPrecipMobjState(mobj, mobj->info->spawnstate);
if (!P_SetPrecipMobjState(mobj, mobj->info->spawnstate))
return false;
mobj->z = (flip) ? (mobj->floorz) : (mobj->ceilingz);
mobj->momz = FixedMul(-mobj->info->speed, mapobjectscale);
mobj->precipflags &= ~PCF_SPLASH;
@ -4213,20 +4221,23 @@ void P_PrecipThinker(precipmobj_t *mobj)
// HACK: sprite changes are 1 tic late, so you would see splashes on the ceiling if not for this state.
// We need to use the settings from the previous state, since some of those are NOT 1 tic late.
INT32 frame = (mobj->frame & ~FF_FRAMEMASK);
P_SetPrecipMobjState(mobj, S_RAINRETURN);
if (!P_SetPrecipMobjState(mobj, S_RAINRETURN))
return false;
mobj->frame = frame;
return;
return true;
}
else
{
if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate))
return;
return false;
}
}
}
if (mobj->precipflags & PCF_SPLASH)
return;
return true;
mobj->z += mobj->momz;
@ -4240,12 +4251,16 @@ void P_PrecipThinker(precipmobj_t *mobj)
}
else
{
P_SetPrecipMobjState(mobj, mobj->info->deathstate);
if (!P_SetPrecipMobjState(mobj, mobj->info->deathstate))
return false;
mobj->z = (flip) ? (mobj->ceilingz) : (mobj->floorz);
mobj->precipflags |= PCF_SPLASH;
R_ResetPrecipitationMobjInterpolationState(mobj);
}
}
return true;
}
static void P_RingThinker(mobj_t *mobj)
@ -11253,7 +11268,7 @@ boolean P_MobjWasRemoved(mobj_t *mobj)
return true;
}
void P_RemovePrecipMobj(precipmobj_t *mobj)
void P_FreePrecipMobj(precipmobj_t *mobj)
{
// unlink from sector and block lists
P_UnsetPrecipThingPosition(mobj);
@ -11265,7 +11280,9 @@ void P_RemovePrecipMobj(precipmobj_t *mobj)
}
// free block
P_RemoveThinker((thinker_t *)mobj);
// Precipmobjs don't actually think using their thinker,
// so the free cannot be delayed.
P_UnlinkThinker((thinker_t*)mobj);
}
// Clearing out stuff for savegames
@ -11301,12 +11318,7 @@ void P_RemoveSavegameMobj(mobj_t *mobj)
// free block
// Here we use the same code as R_RemoveThinkerDelayed, but without reference counting (we're removing everything so it shouldn't matter) and without touching currentthinker since we aren't in P_RunThinkers
{
thinker_t *thinker = (thinker_t *)mobj;
thinker_t *next = thinker->next;
(next->prev = thinker->prev)->next = next;
Z_Free(thinker);
}
P_UnlinkThinker((thinker_t*)mobj);
}
static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};

View file

@ -493,6 +493,8 @@ struct precipmobj_t
INT32 tics; // state tic counter
state_t *state;
UINT32 flags; // flags from mobjinfo tables
tic_t lastThink;
};
struct actioncache_t
@ -540,9 +542,9 @@ void P_RemoveFloorSpriteSlope(mobj_t *mobj);
boolean P_BossTargetPlayer(mobj_t *actor, boolean closest);
boolean P_SupermanLook4Players(mobj_t *actor);
void P_DestroyRobots(void);
void P_PrecipThinker(precipmobj_t *mobj);
boolean P_PrecipThinker(precipmobj_t *mobj);
void P_NullPrecipThinker(precipmobj_t *mobj);
void P_RemovePrecipMobj(precipmobj_t *mobj);
void P_FreePrecipMobj(precipmobj_t *mobj);
void P_SetScale(mobj_t *mobj, fixed_t newscale);
void P_XYMovement(mobj_t *mo);
void P_RingXYMovement(mobj_t *mo);

View file

@ -1833,16 +1833,18 @@ void P_SwitchWeather(preciptype_t newWeather)
if (purge == true)
{
thinker_t *think;
thinker_t *next;
precipmobj_t *precipmobj;
for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next)
for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = next)
{
if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
continue; // not a precipmobj thinker
precipmobj = (precipmobj_t *)think;
P_RemovePrecipMobj(precipmobj);
next = think->next;
P_FreePrecipMobj(precipmobj);
}
}
else if (swap != MT_NULL) // Rather than respawn all that crap, reuse it!

View file

@ -295,8 +295,6 @@ static thinker_t *currentthinker;
//
void P_RemoveThinkerDelayed(thinker_t *thinker)
{
thinker_t *next;
if (thinker->references != 0)
{
#ifdef PARANOIA
@ -327,15 +325,30 @@ void P_RemoveThinkerDelayed(thinker_t *thinker)
return;
}
/* Remove from main thinker list */
next = thinker->next;
R_DestroyLevelInterpolators(thinker);
/* Note that currentthinker is guaranteed to point to us,
* and since we're freeing our memory, we had better change that. So
* point it to thinker->prev, so the iterator will correctly move on to
* thinker->prev->next = thinker->next */
(next->prev = currentthinker = thinker->prev)->next = next;
currentthinker = thinker->prev;
R_DestroyLevelInterpolators(thinker);
/* Remove from main thinker list */
P_UnlinkThinker(thinker);
}
//
// P_UnlinkThinker()
//
// Actually removes thinker from the list and frees its memory.
//
void P_UnlinkThinker(thinker_t *thinker)
{
thinker_t *next = thinker->next;
I_Assert(thinker->references == 0);
(next->prev = thinker->prev)->next = next;
Z_Free(thinker);
}
@ -437,7 +450,7 @@ static void P_RunThinkers(void)
{
size_t i;
for (i = 0; i < NUM_THINKERLISTS; i++)
for (i = 0; i < NUM_ACTIVETHINKERLISTS; i++)
{
ps_thlist_times[i] = I_GetPreciseTime();
for (currentthinker = thlist[i].next; currentthinker != &thlist[i]; currentthinker = currentthinker->next)

View file

@ -2465,6 +2465,12 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
// uncapped/interpolation
interpmobjstate_t interp = {0};
// okay... this is a hack, but weather isn't networked, so it should be ok
if (!P_PrecipThinker(thing))
{
return;
}
// do interpolation
if (R_UsingFrameInterpolation() && !paused)
{
@ -2554,7 +2560,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
if (thing->subsector->sector->cullheight)
{
if (R_DoCulling(thing->subsector->sector->cullheight, viewsector->cullheight, viewz, gz, gzt))
goto weatherthink;
return;
}
// Determine the blendmode and translucency value
@ -2565,7 +2571,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
trans = (thing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
if (trans >= NUMTRANSMAPS)
goto weatherthink; // cap
return; // cap
}
// store information in a vissprite
@ -2623,14 +2629,6 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
// Fullbright
vis->colormap = colormaps;
weatherthink:
// okay... this is a hack, but weather isn't networked, so it should be ok
if (!(thing->precipflags & PCF_THUNK))
{
P_PrecipThinker(thing);
thing->precipflags |= PCF_THUNK;
}
}
// R_AddSprites