Fix severe performance issues in large maps.
So... Remember that one decision I made about avoiding BlockThingsIterator as much as possible? Turns out that was a stupid idea. There ARE situations where it's better to iterate sector thinglists, yes, especially for things that are NOT part of the blockmap, but in other cases, the excess allocations of new iterators are a reasonable price to pay for the lower perf impact in extreme cases, such as maps that have a gazillion sectors with gazillions of things in them (I'm looking at you, UDMF mappers). As a compromise, at least, in situations where the thinglists are needed, I did add a sort of micro-optimization by implementing code to check if a bounding box is inside a sector (would be nice if this was part of GZDoom itself, tho).
This commit is contained in:
parent
318e9b526c
commit
e5e6ce619c
20 changed files with 441 additions and 277 deletions
|
|
@ -102,15 +102,21 @@ extend Class SWWMHandler
|
|||
if ( !swwm_debugview ) return;
|
||||
// prepare projection data, we're going to need this
|
||||
SWWMUtility.PrepareProjData(projdata,e.ViewPos,e.ViewAngle,e.ViewPitch,e.ViewRoll,players[consoleplayer].fov);
|
||||
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
|
||||
foreach ( s:level.Sectors )
|
||||
{
|
||||
if ( (a == players[consoleplayer].Camera) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue;
|
||||
if ( a.bINVISIBLE && !(a is 'DynamicLight') ) continue;
|
||||
if ( (a is 'Inventory') && Inventory(a).Owner ) continue;
|
||||
if ( (a is 'SWWMPickupFlash') && (a.CurState == a.FindState('Pickup')) ) continue;
|
||||
if ( (a is 'SWWMShadow') || (a is 'SWWMItemOverlay') || (a is 'HeadpatTracker') || (a is 'SWWMTeleportLine') || (a is 'SWWMTeleportDest') ) continue;
|
||||
if ( a.Distance3DSquared(e.Camera) > 1000000 ) continue;
|
||||
DrawActor(e,a);
|
||||
// don't check sectors that aren't within bounds, saves some time
|
||||
if ( !BoxInSectorBounds(s,players[consoleplayer].Camera.pos.xy,1000,players[consoleplayer].Camera.CurSector.PortalGroup) )
|
||||
continue;
|
||||
for ( Actor a=s.thinglist; a; a=a.snext )
|
||||
{
|
||||
if ( (a == players[consoleplayer].Camera) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue;
|
||||
if ( a.bINVISIBLE && !(a is 'DynamicLight') ) continue;
|
||||
if ( (a is 'Inventory') && Inventory(a).Owner ) continue;
|
||||
if ( (a is 'SWWMPickupFlash') && (a.CurState == a.FindState('Pickup')) ) continue;
|
||||
if ( (a is 'SWWMShadow') || (a is 'SWWMItemOverlay') || (a is 'HeadpatTracker') || (a is 'SWWMTeleportLine') || (a is 'SWWMTeleportDest') ) continue;
|
||||
if ( a.Distance3DSquared(e.Camera) > 1000000 ) continue;
|
||||
DrawActor(e,a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,42 @@ Class RampancyLogonDummy : Actor
|
|||
}
|
||||
}
|
||||
|
||||
// this is used to speed up iteration through sector thinglists within a specific area
|
||||
Class SectorBounds
|
||||
{
|
||||
Vector4 bounds;
|
||||
int portalgroup;
|
||||
|
||||
clearscope bool PointInSectorBounds( Vector2 p, int pg = -1 ) const
|
||||
{
|
||||
if ( (pg >= 0) && (level.GetPortalGroupCount() > 0) && (pg != portalgroup) )
|
||||
p += level.GetDisplacement(pg,portalgroup);
|
||||
if ( p.x < bounds.x ) return false;
|
||||
if ( p.y < bounds.y ) return false;
|
||||
if ( p.x > bounds.z ) return false;
|
||||
if ( p.y > bounds.w ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
clearscope bool BoxInSectorBounds( Vector2 p, double r, int pg = -1 ) const
|
||||
{
|
||||
if ( (pg >= 0) && (level.GetPortalGroupCount() > 0) && (pg != portalgroup) )
|
||||
p += level.GetDisplacement(pg,portalgroup);
|
||||
if ( p.x+r < bounds.x ) return false;
|
||||
if ( p.y+r < bounds.y ) return false;
|
||||
if ( p.x-r > bounds.z ) return false;
|
||||
if ( p.y-r > bounds.w ) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extend Class SWWMHandler
|
||||
{
|
||||
bool maphaskeys;
|
||||
|
||||
// weird optimization
|
||||
Array<SectorBounds> sbounds;
|
||||
|
||||
// level end stats
|
||||
override void WorldUnloaded( WorldEvent e )
|
||||
{
|
||||
|
|
@ -146,6 +178,51 @@ extend Class SWWMHandler
|
|||
SWWMUtility.MarkAchievement("hurry",players[consoleplayer]);
|
||||
}
|
||||
|
||||
clearscope bool PointInSectorBounds( Sector s, Vector2 p, int pg = -1 ) const
|
||||
{
|
||||
if ( sbounds.Size() <= 0 ) return false;
|
||||
int i = s.Index();
|
||||
return sbounds[i].PointInSectorBounds(p,pg);
|
||||
}
|
||||
|
||||
clearscope bool BoxInSectorBounds( Sector s, Vector2 p, double r, int pg = -1 ) const
|
||||
{
|
||||
if ( sbounds.Size() <= 0 ) return false;
|
||||
int i = s.Index();
|
||||
return sbounds[i].BoxInSectorBounds(p,r,pg);
|
||||
}
|
||||
|
||||
void PrecalculateSectorBounds()
|
||||
{
|
||||
sbounds.Resize(level.Sectors.Size());
|
||||
foreach ( s:level.Sectors )
|
||||
{
|
||||
let sb = new("SectorBounds");
|
||||
sb.portalgroup = s.portalgroup;
|
||||
sb.bounds = ( 32767, 32767, -32768, -32768 );
|
||||
foreach ( l:s.Lines )
|
||||
{
|
||||
if ( l.v1.p.x < sb.bounds.x )
|
||||
sb.bounds.x = l.v1.p.x;
|
||||
if ( l.v2.p.x < sb.bounds.x )
|
||||
sb.bounds.x = l.v2.p.x;
|
||||
if ( l.v1.p.y < sb.bounds.y )
|
||||
sb.bounds.y = l.v1.p.y;
|
||||
if ( l.v2.p.y < sb.bounds.y )
|
||||
sb.bounds.y = l.v2.p.y;
|
||||
if ( l.v1.p.x > sb.bounds.z )
|
||||
sb.bounds.z = l.v1.p.x;
|
||||
if ( l.v2.p.x > sb.bounds.z )
|
||||
sb.bounds.z = l.v2.p.x;
|
||||
if ( l.v1.p.y > sb.bounds.w )
|
||||
sb.bounds.w = l.v1.p.y;
|
||||
if ( l.v2.p.y > sb.bounds.w )
|
||||
sb.bounds.w = l.v2.p.y;
|
||||
}
|
||||
sbounds[s.Index()] = sb;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapStartDialogues()
|
||||
{
|
||||
int whichboss = WhichVanillaBossMap();
|
||||
|
|
@ -264,6 +341,7 @@ extend Class SWWMHandler
|
|||
// (if we do it in OnRegister the existing thinker might not have been deserialized yet)
|
||||
gdat = SWWMGlobals.Get();
|
||||
if ( e.IsReopen ) return;
|
||||
PrecalculateSectorBounds();
|
||||
MapStartDialogues();
|
||||
if ( gamestate != GS_TITLELEVEL )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -172,18 +172,12 @@ extend Class SWWMHandler
|
|||
// copies the floatbob of overlapping identical items, so it doesn't look weird
|
||||
private void CopyFloatBob( Actor a )
|
||||
{
|
||||
bool breakout = false;
|
||||
foreach ( s:level.Sectors )
|
||||
for ( Actor t=a.CurSector.thinglist; t; t=t.snext )
|
||||
{
|
||||
for ( Actor t=s.thinglist; t; t=t.snext )
|
||||
{
|
||||
if ( (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
|
||||
a.floatbobphase = t.floatbobphase;
|
||||
a.angle = t.angle; // also copy angle
|
||||
breakout = true;
|
||||
break;
|
||||
}
|
||||
if ( breakout ) break;
|
||||
if ( (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
|
||||
a.floatbobphase = t.floatbobphase;
|
||||
a.angle = t.angle; // also copy angle
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -281,42 +281,48 @@ extend Class SWWMHandler
|
|||
double viewdist = SWWMStatusBar.MAPVIEWDIST;
|
||||
// still about as expensive as using a BlockThingsIterator, but without the need to allocate one every tic
|
||||
int thisgroup = players[consoleplayer].Camera.CurSector.portalgroup;
|
||||
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
|
||||
foreach ( s:level.Sectors )
|
||||
{
|
||||
Vector2 rv;
|
||||
if ( s.portalgroup != thisgroup )
|
||||
// don't check sectors that aren't within bounds, saves some time
|
||||
if ( !BoxInSectorBounds(s,players[consoleplayer].Camera.pos.xy,viewdist,players[consoleplayer].Camera.CurSector.PortalGroup) )
|
||||
continue;
|
||||
for ( Actor a=s.thinglist; a; a=a.snext )
|
||||
{
|
||||
Vector2 relpos = players[consoleplayer].Camera.pos.xy+level.GetDisplacement(thisgroup,s.portalgroup);
|
||||
rv = a.pos.xy-relpos;
|
||||
Vector2 rv;
|
||||
if ( s.portalgroup != thisgroup )
|
||||
{
|
||||
Vector2 relpos = players[consoleplayer].Camera.pos.xy+level.GetDisplacement(thisgroup,s.portalgroup);
|
||||
rv = a.pos.xy-relpos;
|
||||
}
|
||||
else rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
|
||||
double rad;
|
||||
bool isproj = a.bMISSILE&&SWWMUtility.ValidProjectile(a);
|
||||
if ( SWWMUtility.IsBeamProj(a) )
|
||||
{
|
||||
isproj = true;
|
||||
rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
|
||||
}
|
||||
else rad = a.radius;
|
||||
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
|
||||
continue;
|
||||
if ( a == players[consoleplayer].Camera )
|
||||
continue;
|
||||
if ( a is 'GhostTarget' )
|
||||
continue;
|
||||
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') && !isproj )
|
||||
continue;
|
||||
if ( !level.allmap && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !(a.IsFriend(players[consoleplayer].mo) && !(a.player && (a.player.mo != a))) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
||||
continue;
|
||||
if ( a.bKILLED || (a.Health <= 0) || a.bUnmorphed )
|
||||
continue;
|
||||
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner || (a.GetClassName() == 'aas_token')) ) // autoautosave hotfix
|
||||
continue;
|
||||
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
|
||||
continue;
|
||||
if ( isproj && !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
||||
continue;
|
||||
SWWMSimpleTracker.Track(self,a);
|
||||
}
|
||||
else rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
|
||||
double rad;
|
||||
bool isproj = a.bMISSILE&&SWWMUtility.ValidProjectile(a);
|
||||
if ( SWWMUtility.IsBeamProj(a) )
|
||||
{
|
||||
isproj = true;
|
||||
rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
|
||||
}
|
||||
else rad = a.radius;
|
||||
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
|
||||
continue;
|
||||
if ( a == players[consoleplayer].Camera )
|
||||
continue;
|
||||
if ( a is 'GhostTarget' )
|
||||
continue;
|
||||
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') && !isproj )
|
||||
continue;
|
||||
if ( !level.allmap && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !(a.IsFriend(players[consoleplayer].mo) && !(a.player && (a.player.mo != a))) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
||||
continue;
|
||||
if ( a.bKILLED || (a.Health <= 0) || a.bUnmorphed )
|
||||
continue;
|
||||
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner || (a.GetClassName() == 'aas_token')) ) // autoautosave hotfix
|
||||
continue;
|
||||
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
|
||||
continue;
|
||||
if ( isproj && !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
|
||||
continue;
|
||||
SWWMSimpleTracker.Track(self,a);
|
||||
}
|
||||
SWWMSimpleTracker trk = strackers;
|
||||
SWWMSimpleTracker prev = null, next;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ Class SWWMStaticHandler : StaticEventHandler
|
|||
SWWMHandler.ClearAllShaders();
|
||||
EventHandler.SendInterfaceEvent(consoleplayer,"swwmflushhud");
|
||||
EventHandler.SendInterfaceEvent(consoleplayer,"swwmaprcheck");
|
||||
// quick fix for old savegames
|
||||
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
|
||||
if ( hnd && (hnd.sbounds.Size() <= 0) )
|
||||
hnd.PrecalculateSectorBounds();
|
||||
if ( !e.IsSaveGame ) return;
|
||||
// save version checker
|
||||
tainted = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue