1
0
forked from 0ad/0ad

Optimise tile-based pathfinder, particularly for large maps.

This was SVN commit r9000.
This commit is contained in:
Ykkrosh 2011-02-28 00:35:53 +00:00
parent cb0e322a61
commit 794584ea11
4 changed files with 100 additions and 11 deletions

View File

@ -123,6 +123,8 @@ const int COST_CLASS_BITS = 16 - (PASS_CLASS_BITS + 2);
#define GET_COST_CLASS(item) ((item) >> (PASS_CLASS_BITS + 2))
#define COST_CLASS_MASK(id) ( (TerrainTile) ((id) << (PASS_CLASS_BITS + 2)) )
typedef SparseGrid<PathfindTile> PathfindTileGrid;
struct AsyncLongPathRequest
{
u32 ticket;
@ -187,7 +189,7 @@ public:
bool m_TerrainDirty; // indicates if m_Grid has been updated since terrain changed
// Debugging - output from last pathfind operation:
Grid<PathfindTile>* m_DebugGrid;
PathfindTileGrid* m_DebugGrid;
u32 m_DebugSteps;
Path* m_DebugPath;
PathfinderOverlay* m_DebugOverlay;

View File

@ -191,7 +191,7 @@ struct PathfinderState
PriorityQueue open;
// (there's no explicit closed list; it's encoded in PathfindTile)
Grid<PathfindTile>* tiles;
PathfindTileGrid* tiles;
Grid<TerrainTile>* terrain;
bool ignoreImpassable; // allows us to escape if stuck in patches of impassability
@ -249,7 +249,7 @@ static u32 CalculateHeuristic(u16 i, u16 j, u16 iGoal, u16 jGoal, u16 rGoal)
}
// Calculate movement cost from predecessor tile pi,pj to tile i,j
static u32 CalculateCostDelta(u16 pi, u16 pj, u16 i, u16 j, Grid<PathfindTile>* tempGrid, u32 tileCost)
static u32 CalculateCostDelta(u16 pi, u16 pj, u16 i, u16 j, PathfindTileGrid* tempGrid, u32 tileCost)
{
u32 dg = tileCost;
@ -391,7 +391,7 @@ void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, const Goal& g
state.steps = 0;
state.tiles = new Grid<PathfindTile>(m_MapSize, m_MapSize);
state.tiles = new PathfindTileGrid(m_MapSize, m_MapSize);
state.terrain = m_Grid;
state.iBest = i0;

View File

@ -22,6 +22,9 @@
#include "graphics/MapReader.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureManager.h"
#include "lib/timer.h"
#include "lib/tex/tex.h"
#include "ps/Loader.h"
#include "simulation2/Simulation2.h"
@ -35,10 +38,19 @@ public:
g_VFS = CreateVfs(20 * MiB);
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/L"mods/public", VFS_MOUNT_MUST_EXIST));
TS_ASSERT_OK(g_VFS->Mount(L"cache/", DataDir()/L"cache"));
// Need some stuff for terrain movement costs:
// (TODO: this ought to be independent of any graphics code)
tex_codec_register_all();
new CTerrainTextureManager;
g_TexMan.LoadTerrainTextures();
}
void tearDown()
{
delete &g_TexMan;
tex_codec_unregister_all();
g_VFS.reset();
CXeromyces::Terminate();
@ -56,7 +68,7 @@ public:
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
LDR_BeginRegistering();
mapReader->LoadMap(L"maps/scenarios/Latium.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, -1);
mapReader->LoadMap(L"maps/scenarios/Median Oasis.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, -1);
LDR_EndRegistering();
TS_ASSERT_OK(LDR_NonprogressiveLoad());
@ -69,26 +81,31 @@ public:
entity_pos_t z0 = entity_pos_t::FromInt(495);
entity_pos_t x1 = entity_pos_t::FromInt(500);
entity_pos_t z1 = entity_pos_t::FromInt(495);
ICmpPathfinder::Goal goal = { x1, z1, entity_pos_t::FromInt(0), entity_pos_t::FromInt(0) };
ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, x1, z1 };
ICmpPathfinder::Path path;
cmp->ComputePath(x0, z0, goal, path);
cmp->ComputePath(x0, z0, goal, cmp->GetPassabilityClass("default"), cmp->GetCostClass("default"), path);
for (size_t i = 0; i < path.m_Waypoints.size(); ++i)
printf("%d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToDouble(), path.m_Waypoints[i].z.ToDouble());
#endif
double t = timer_Time();
srand(1234);
for (size_t j = 0; j < 2560; ++j)
for (size_t j = 0; j < 1024*2; ++j)
{
entity_pos_t x0 = entity_pos_t::FromInt(rand() % 512);
entity_pos_t z0 = entity_pos_t::FromInt(rand() % 512);
entity_pos_t x1 = entity_pos_t::FromInt(rand() % 512);
entity_pos_t z1 = entity_pos_t::FromInt(rand() % 512);
entity_pos_t x1 = x0 + entity_pos_t::FromInt(rand() % 64);
entity_pos_t z1 = z0 + entity_pos_t::FromInt(rand() % 64);
ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, x1, z1 };
ICmpPathfinder::Path path;
cmp->ComputePath(x0, z0, goal, cmp->GetPassabilityClass("default"), cmp->GetCostClass("default"), path);
}
t = timer_Time() - t;
printf("[%f]", t);
}
void test_performance_short_DISABLED()

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -34,6 +34,7 @@
template<typename T>
class Grid
{
NONCOPYABLE(Grid);
public:
Grid(u16 w, u16 h) : m_W(w), m_H(h), m_DirtyID(0)
{
@ -73,4 +74,73 @@ public:
size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated
};
/**
* Similar to Grid, except optimised for sparse usage (the grid is subdivided into
* buckets whose contents are only initialised on demand, to save on memset cost).
*/
template<typename T>
class SparseGrid
{
NONCOPYABLE(SparseGrid);
enum { BucketBits = 4, BucketSize = 1 << BucketBits };
T* GetBucket(size_t i, size_t j)
{
size_t b = (j >> BucketBits) * m_BW + (i >> BucketBits);
if (!m_Data[b])
{
m_Data[b] = new T[BucketSize*BucketSize];
memset(m_Data[b], 0, BucketSize*BucketSize*sizeof(T));
}
return m_Data[b];
}
public:
SparseGrid(u16 w, u16 h) : m_W(w), m_H(h), m_DirtyID(0)
{
m_BW = (m_W + BucketSize-1) >> BucketBits;
m_BH = (m_H + BucketSize-1) >> BucketBits;
m_Data = new T*[m_BW*m_BH];
memset(m_Data, 0, m_BW*m_BH*sizeof(T*));
}
~SparseGrid()
{
reset();
delete[] m_Data;
}
void reset()
{
for (size_t i = 0; i < (size_t)(m_BW*m_BH); ++i)
delete[] m_Data[i];
memset(m_Data, 0, m_BW*m_BH*sizeof(T*));
}
void set(size_t i, size_t j, const T& value)
{
#if GRID_BOUNDS_DEBUG
debug_assert(i < m_W && j < m_H);
#endif
GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)] = value;
}
T& get(size_t i, size_t j)
{
#if GRID_BOUNDS_DEBUG
debug_assert(i < m_W && j < m_H);
#endif
return GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)];
}
u16 m_W, m_H;
u16 m_BW, m_BH;
T** m_Data;
size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated
};
#endif // INCLUDED_GRID