1
0
forked from 0ad/0ad

Optimise obstruction grid updates in the common case when terrain hasn't changed

This was SVN commit r9001.
This commit is contained in:
Ykkrosh 2011-02-28 01:24:12 +00:00
parent 794584ea11
commit dbc6d27411
2 changed files with 71 additions and 24 deletions

View File

@ -216,7 +216,7 @@ public:
m_WorldZ0 = z0;
m_WorldX1 = x1;
m_WorldZ1 = z1;
MakeDirty();
MakeDirtyAll();
// Subdivision system bounds:
debug_assert(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
@ -250,7 +250,7 @@ public:
UnitShape shape = { ent, x, z, r, flags, group };
size_t id = m_UnitShapeNext++;
m_UnitShapes[id] = shape;
MakeDirtyUnits();
MakeDirtyUnit(flags);
m_UnitSubdivision.Add(id, CFixedVector2D(x - r, z - r), CFixedVector2D(x + r, z + r));
@ -267,7 +267,7 @@ public:
StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags };
size_t id = m_StaticShapeNext++;
m_StaticShapes[id] = shape;
MakeDirty();
MakeDirtyStatic(flags);
CFixedVector2D center(x, z);
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
@ -312,7 +312,7 @@ public:
shape.x = x;
shape.z = z;
MakeDirtyUnits();
MakeDirtyUnit(shape.flags);
}
else
{
@ -336,7 +336,7 @@ public:
shape.u = u;
shape.v = v;
MakeDirty();
MakeDirtyStatic(shape.flags);
}
}
@ -351,8 +351,6 @@ public:
shape.flags |= FLAG_MOVING;
else
shape.flags &= ~FLAG_MOVING;
MakeDirtyUnits();
}
}
@ -364,7 +362,6 @@ public:
{
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
shape.group = group;
MakeDirtyUnits();
}
}
@ -379,8 +376,8 @@ public:
CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
CFixedVector2D(shape.x + shape.r, shape.z + shape.r));
MakeDirtyUnit(shape.flags);
m_UnitShapes.erase(TAG_TO_INDEX(tag));
MakeDirtyUnits();
}
else
{
@ -390,8 +387,8 @@ public:
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
MakeDirtyStatic(shape.flags);
m_StaticShapes.erase(TAG_TO_INDEX(tag));
MakeDirty();
}
}
@ -426,7 +423,7 @@ public:
virtual void SetPassabilityCircular(bool enabled)
{
m_PassabilityCircular = enabled;
MakeDirty();
MakeDirtyAll();
}
virtual void SetDebugOverlay(bool enabled)
@ -448,20 +445,35 @@ private:
/**
* Mark all previous Rasterise()d grids as dirty, and the debug display.
* Call this when any static shapes or world bounds have changed.
* Call this when the world bounds have changed.
*/
void MakeDirty()
void MakeDirtyAll()
{
++m_DirtyID;
m_DebugOverlayDirty = true;
}
/**
* Mark the debug display as dirty.
* Call this when any unit shapes (which don't affect Rasterise) have changed.
* Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
* Call this when a static shape has changed.
*/
void MakeDirtyUnits()
void MakeDirtyStatic(u8 flags)
{
if (flags & (FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION))
++m_DirtyID;
m_DebugOverlayDirty = true;
}
/**
* Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
* Call this when a unit shape has changed.
*/
void MakeDirtyUnit(u8 flags)
{
if (flags & (FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION))
++m_DirtyID;
m_DebugOverlayDirty = true;
}

View File

@ -277,8 +277,6 @@ const Grid<u16>& CCmpPathfinder::GetPassabilityGrid()
void CCmpPathfinder::UpdateGrid()
{
PROFILE("UpdateGrid");
// Initialise the terrain data when first needed
if (!m_Grid)
{
@ -291,16 +289,53 @@ void CCmpPathfinder::UpdateGrid()
m_ObstructionGrid = new Grid<u8>(m_MapSize, m_MapSize);
}
CmpPtr<ICmpWaterManager> cmpWaterMan(GetSimContext(), SYSTEM_ENTITY);
CTerrain& terrain = GetSimContext().GetTerrain();
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (cmpObstructionManager->Rasterise(*m_ObstructionGrid) || m_TerrainDirty)
bool obstructionsDirty = cmpObstructionManager->Rasterise(*m_ObstructionGrid);
if (obstructionsDirty && !m_TerrainDirty)
{
PROFILE("UpdateGrid obstructions");
// Obstructions changed - we need to recompute passability
// Since terrain hasn't changed we only need to update the obstruction bits
// and can skip the rest of the data
// TODO: if ObstructionManager::SetPassabilityCircular was called at runtime
// (which should probably never happen, but that's not guaranteed),
// then TILE_OUTOFBOUNDS will change and we can't use this fast path, but
// currently it'll just set obstructionsDirty and we won't notice
for (u16 j = 0; j < m_MapSize; ++j)
{
for (u16 i = 0; i < m_MapSize; ++i)
{
TerrainTile& t = m_Grid->get(i, j);
u8 obstruct = m_ObstructionGrid->get(i, j);
if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING)
t |= 1;
else
t &= ~1;
if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION)
t |= 2;
else
t &= ~2;
}
}
}
else if (obstructionsDirty || m_TerrainDirty)
{
PROFILE("UpdateGrid full");
// Obstructions or terrain changed - we need to recompute passability
// TODO: only bother recomputing the region that has actually changed
// TODO: if only obstructions changed, don't recompute the terrain masks
CmpPtr<ICmpWaterManager> cmpWaterMan(GetSimContext(), SYSTEM_ENTITY);
CTerrain& terrain = GetSimContext().GetTerrain();
for (u16 j = 0; j < m_MapSize; ++j)
{