mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Move path computations to an actual worker to prepare for threading.
This moves the "async" pathfinding computations to a worker, preparing the architecture for threading. Tested By: Kuba386, Stan` Differential Revision: https://code.wildfiregames.com/D1918 This was SVN commit r22902.
This commit is contained in:
parent
2333b1814e
commit
d592bf9cb6
6 changed files with 198 additions and 141 deletions
|
|
@ -540,8 +540,8 @@ void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengt
|
|||
CmpPtr<ICmpPathfinder> cmpPathfinder(simContext, SYSTEM_ENTITY);
|
||||
if (cmpPathfinder)
|
||||
{
|
||||
cmpPathfinder->FetchAsyncResultsAndSendMessages();
|
||||
cmpPathfinder->UpdateGrid();
|
||||
cmpPathfinder->FinishAsyncRequests();
|
||||
}
|
||||
|
||||
// Push AI commands onto the queue before we use them
|
||||
|
|
@ -555,14 +555,17 @@ void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengt
|
|||
|
||||
// Process newly generated move commands so the UI feels snappy
|
||||
if (cmpPathfinder)
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
{
|
||||
cmpPathfinder->StartProcessingMoves(true);
|
||||
cmpPathfinder->FetchAsyncResultsAndSendMessages();
|
||||
}
|
||||
// Send all the update phases
|
||||
{
|
||||
PROFILE2("Sim - Update");
|
||||
CMessageUpdate msgUpdate(turnLengthFixed);
|
||||
componentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
|
||||
{
|
||||
CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed);
|
||||
componentManager.BroadcastMessage(msgUpdate);
|
||||
|
|
@ -570,7 +573,10 @@ void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengt
|
|||
|
||||
// Process move commands for formations (group proxy)
|
||||
if (cmpPathfinder)
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
{
|
||||
cmpPathfinder->StartProcessingMoves(true);
|
||||
cmpPathfinder->FetchAsyncResultsAndSendMessages();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE2("Sim - Motion Unit");
|
||||
|
|
@ -583,12 +589,12 @@ void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengt
|
|||
componentManager.BroadcastMessage(msgUpdate);
|
||||
}
|
||||
|
||||
// Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others
|
||||
if (cmpPathfinder)
|
||||
cmpPathfinder->ProcessSameTurnMoves();
|
||||
|
||||
// Clean up any entities destroyed during the simulation update
|
||||
componentManager.FlushDestroyedComponents();
|
||||
|
||||
// Process all remaining moves
|
||||
if (cmpPathfinder)
|
||||
cmpPathfinder->StartProcessingMoves(false);
|
||||
}
|
||||
|
||||
void CSimulation2Impl::Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
|
||||
|
|
|
|||
|
|
@ -55,8 +55,6 @@ void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode))
|
|||
|
||||
m_AtlasOverlay = NULL;
|
||||
|
||||
m_SameTurnMovesCount = 0;
|
||||
|
||||
m_VertexPathfinder = std::unique_ptr<VertexPathfinder>(new VertexPathfinder(m_MapSize, m_TerrainOnlyGrid));
|
||||
m_LongPathfinder = std::unique_ptr<LongPathfinder>(new LongPathfinder());
|
||||
m_PathfinderHier = std::unique_ptr<HierarchicalPathfinder>(new HierarchicalPathfinder());
|
||||
|
|
@ -98,12 +96,16 @@ void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode))
|
|||
m_PassClasses.push_back(PathfinderPassability(mask, it->second));
|
||||
m_PassClassMasks[name] = mask;
|
||||
}
|
||||
|
||||
m_Workers.emplace_back(PathfinderWorker{});
|
||||
}
|
||||
|
||||
CCmpPathfinder::~CCmpPathfinder() {};
|
||||
|
||||
void CCmpPathfinder::Deinit()
|
||||
{
|
||||
m_Workers.clear();
|
||||
|
||||
SetDebugOverlay(false); // cleans up memory
|
||||
SAFE_DELETE(m_AtlasOverlay);
|
||||
|
||||
|
|
@ -149,7 +151,6 @@ void CCmpPathfinder::SerializeCommon(S& serialize)
|
|||
SerializeVector<SerializeLongRequest>()(serialize, "long requests", m_LongPathRequests);
|
||||
SerializeVector<SerializeShortRequest>()(serialize, "short requests", m_ShortPathRequests);
|
||||
serialize.NumberU32_Unbounded("next ticket", m_NextAsyncTicket);
|
||||
serialize.NumberU16_Unbounded("same turn moves count", m_SameTurnMovesCount);
|
||||
serialize.NumberU16_Unbounded("map size", m_MapSize);
|
||||
}
|
||||
|
||||
|
|
@ -184,9 +185,6 @@ void CCmpPathfinder::HandleMessage(const CMessage& msg, bool UNUSED(global))
|
|||
m_TerrainDirty = true;
|
||||
UpdateGrid();
|
||||
break;
|
||||
case MT_TurnStart:
|
||||
m_SameTurnMovesCount = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -703,10 +701,46 @@ void CCmpPathfinder::TerrainUpdateHelper(bool expandPassability/* = true */)
|
|||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
// Async pathfinder workers
|
||||
|
||||
void CCmpPathfinder::ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) const
|
||||
CCmpPathfinder::PathfinderWorker::PathfinderWorker() {}
|
||||
|
||||
template<typename T>
|
||||
void CCmpPathfinder::PathfinderWorker::PushRequests(std::vector<T>&, ssize_t)
|
||||
{
|
||||
m_LongPathfinder->ComputePath(*m_PathfinderHier, x0, z0, goal, passClass, ret);
|
||||
static_assert(sizeof(T) == 0, "Only specializations can be used");
|
||||
}
|
||||
|
||||
template<> void CCmpPathfinder::PathfinderWorker::PushRequests(std::vector<LongPathRequest>& from, ssize_t amount)
|
||||
{
|
||||
m_LongRequests.insert(m_LongRequests.end(), std::make_move_iterator(from.end() - amount), std::make_move_iterator(from.end()));
|
||||
}
|
||||
|
||||
template<> void CCmpPathfinder::PathfinderWorker::PushRequests(std::vector<ShortPathRequest>& from, ssize_t amount)
|
||||
{
|
||||
m_ShortRequests.insert(m_ShortRequests.end(), std::make_move_iterator(from.end() - amount), std::make_move_iterator(from.end()));
|
||||
}
|
||||
|
||||
void CCmpPathfinder::PathfinderWorker::Work(const CCmpPathfinder& pathfinder)
|
||||
{
|
||||
while (!m_LongRequests.empty())
|
||||
{
|
||||
const LongPathRequest& req = m_LongRequests.back();
|
||||
WaypointPath path;
|
||||
pathfinder.m_LongPathfinder->ComputePath(*pathfinder.m_PathfinderHier, req.x0, req.z0, req.goal, req.passClass, path);
|
||||
m_Results.emplace_back(req.ticket, req.notify, path);
|
||||
|
||||
m_LongRequests.pop_back();
|
||||
}
|
||||
|
||||
while (!m_ShortRequests.empty())
|
||||
{
|
||||
const ShortPathRequest& req = m_ShortRequests.back();
|
||||
WaypointPath path = pathfinder.m_VertexPathfinder->ComputeShortPath(req, CmpPtr<ICmpObstructionManager>(pathfinder.GetSystemEntity()));
|
||||
m_Results.emplace_back(req.ticket, req.notify, path);
|
||||
|
||||
m_ShortRequests.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
u32 CCmpPathfinder::ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, entity_id_t notify)
|
||||
|
|
@ -716,118 +750,98 @@ u32 CCmpPathfinder::ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const Pat
|
|||
return req.ticket;
|
||||
}
|
||||
|
||||
u32 CCmpPathfinder::ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t clearance, entity_pos_t range, const PathGoal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t group, entity_id_t notify)
|
||||
u32 CCmpPathfinder::ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t clearance, entity_pos_t range,
|
||||
const PathGoal& goal, pass_class_t passClass, bool avoidMovingUnits,
|
||||
entity_id_t group, entity_id_t notify)
|
||||
{
|
||||
ShortPathRequest req = { m_NextAsyncTicket++, x0, z0, clearance, range, goal, passClass, avoidMovingUnits, group, notify };
|
||||
m_ShortPathRequests.push_back(req);
|
||||
return req.ticket;
|
||||
}
|
||||
|
||||
WaypointPath CCmpPathfinder::ComputeShortPath(const ShortPathRequest& request) const
|
||||
void CCmpPathfinder::ComputePathImmediate(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) const
|
||||
{
|
||||
m_LongPathfinder->ComputePath(*m_PathfinderHier, x0, z0, goal, passClass, ret);
|
||||
}
|
||||
|
||||
WaypointPath CCmpPathfinder::ComputeShortPathImmediate(const ShortPathRequest& request) const
|
||||
{
|
||||
return m_VertexPathfinder->ComputeShortPath(request, CmpPtr<ICmpObstructionManager>(GetSystemEntity()));
|
||||
}
|
||||
|
||||
// Async processing:
|
||||
|
||||
void CCmpPathfinder::FinishAsyncRequests()
|
||||
void CCmpPathfinder::FetchAsyncResultsAndSendMessages()
|
||||
{
|
||||
PROFILE2("Finish Async Requests");
|
||||
// Save the request queue in case it gets modified while iterating
|
||||
std::vector<LongPathRequest> longRequests;
|
||||
m_LongPathRequests.swap(longRequests);
|
||||
PROFILE2("FetchAsyncResults");
|
||||
|
||||
std::vector<ShortPathRequest> shortRequests;
|
||||
m_ShortPathRequests.swap(shortRequests);
|
||||
|
||||
// TODO: we should only compute one path per entity per turn
|
||||
|
||||
// TODO: this computation should be done incrementally, spread
|
||||
// across multiple frames (or even multiple turns)
|
||||
|
||||
ProcessLongRequests(longRequests);
|
||||
ProcessShortRequests(shortRequests);
|
||||
}
|
||||
|
||||
void CCmpPathfinder::ProcessLongRequests(const std::vector<LongPathRequest>& longRequests)
|
||||
{
|
||||
PROFILE2("Process Long Requests");
|
||||
for (size_t i = 0; i < longRequests.size(); ++i)
|
||||
// WARNING: the order in which moves are pulled must be consistent when using 1 or n workers.
|
||||
// We fetch in the same order we inserted in, but we push moves backwards, so this works.
|
||||
std::vector<PathResult> results;
|
||||
for (PathfinderWorker& worker : m_Workers)
|
||||
{
|
||||
const LongPathRequest& req = longRequests[i];
|
||||
WaypointPath path;
|
||||
ComputePath(req.x0, req.z0, req.goal, req.passClass, path);
|
||||
CMessagePathResult msg(req.ticket, path);
|
||||
GetSimContext().GetComponentManager().PostMessage(req.notify, msg);
|
||||
results.insert(results.end(), std::make_move_iterator(worker.m_Results.begin()), std::make_move_iterator(worker.m_Results.end()));
|
||||
worker.m_Results.clear();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE2("PostMessages");
|
||||
for (PathResult& path : results)
|
||||
{
|
||||
CMessagePathResult msg(path.ticket, path.path);
|
||||
GetSimContext().GetComponentManager().PostMessage(path.notify, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpPathfinder::ProcessShortRequests(const std::vector<ShortPathRequest>& shortRequests)
|
||||
void CCmpPathfinder::StartProcessingMoves(bool useMax)
|
||||
{
|
||||
PROFILE2("Process Short Requests");
|
||||
for (size_t i = 0; i < shortRequests.size(); ++i)
|
||||
{
|
||||
const ShortPathRequest& req = shortRequests[i];
|
||||
WaypointPath path = m_VertexPathfinder->ComputeShortPath(req, CmpPtr<ICmpObstructionManager>(GetSystemEntity()));
|
||||
CMessagePathResult msg(req.ticket, path);
|
||||
GetSimContext().GetComponentManager().PostMessage(req.notify, msg);
|
||||
}
|
||||
std::vector<LongPathRequest> longRequests = PopMovesToProcess(m_LongPathRequests, useMax, m_MaxSameTurnMoves);
|
||||
std::vector<ShortPathRequest> shortRequests = PopMovesToProcess(m_ShortPathRequests, useMax, m_MaxSameTurnMoves - longRequests.size());
|
||||
|
||||
PushRequestsToWorkers(longRequests);
|
||||
PushRequestsToWorkers(shortRequests);
|
||||
|
||||
for (PathfinderWorker& worker : m_Workers)
|
||||
worker.Work(*this);
|
||||
}
|
||||
|
||||
void CCmpPathfinder::ProcessSameTurnMoves()
|
||||
template <typename T>
|
||||
std::vector<T> CCmpPathfinder::PopMovesToProcess(std::vector<T>& requests, bool useMax, size_t maxMoves)
|
||||
{
|
||||
if (!m_LongPathRequests.empty())
|
||||
std::vector<T> poppedRequests;
|
||||
if (useMax)
|
||||
{
|
||||
// Figure out how many moves we can do this time
|
||||
i32 moveCount = m_MaxSameTurnMoves - m_SameTurnMovesCount;
|
||||
|
||||
if (moveCount <= 0)
|
||||
return;
|
||||
|
||||
// Copy the long request elements we are going to process into a new array
|
||||
std::vector<LongPathRequest> longRequests;
|
||||
if ((i32)m_LongPathRequests.size() <= moveCount)
|
||||
size_t amount = std::min(requests.size(), maxMoves);
|
||||
if (amount > 0)
|
||||
{
|
||||
m_LongPathRequests.swap(longRequests);
|
||||
moveCount = (i32)longRequests.size();
|
||||
poppedRequests.insert(poppedRequests.begin(), std::make_move_iterator(requests.end() - amount), std::make_move_iterator(requests.end()));
|
||||
requests.erase(requests.end() - amount, requests.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
longRequests.resize(moveCount);
|
||||
copy(m_LongPathRequests.begin(), m_LongPathRequests.begin() + moveCount, longRequests.begin());
|
||||
m_LongPathRequests.erase(m_LongPathRequests.begin(), m_LongPathRequests.begin() + moveCount);
|
||||
}
|
||||
|
||||
ProcessLongRequests(longRequests);
|
||||
|
||||
m_SameTurnMovesCount = (u16)(m_SameTurnMovesCount + moveCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
poppedRequests.swap(requests);
|
||||
requests.clear();
|
||||
}
|
||||
|
||||
if (!m_ShortPathRequests.empty())
|
||||
return poppedRequests;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CCmpPathfinder::PushRequestsToWorkers(std::vector<T>& from)
|
||||
{
|
||||
if (from.empty())
|
||||
return;
|
||||
|
||||
// Trivial load-balancing, / rounds towards zero so add 1 to ensure we do push all requests.
|
||||
size_t amount = from.size() / m_Workers.size() + 1;
|
||||
|
||||
// WARNING: the order in which moves are pushed must be consistent when using 1 or n workers.
|
||||
// In this instance, work is distributed in a strict LIFO order, effectively reversing tickets.
|
||||
for (PathfinderWorker& worker : m_Workers)
|
||||
{
|
||||
// Figure out how many moves we can do now
|
||||
i32 moveCount = m_MaxSameTurnMoves - m_SameTurnMovesCount;
|
||||
|
||||
if (moveCount <= 0)
|
||||
return;
|
||||
|
||||
// Copy the short request elements we are going to process into a new array
|
||||
std::vector<ShortPathRequest> shortRequests;
|
||||
if ((i32)m_ShortPathRequests.size() <= moveCount)
|
||||
{
|
||||
m_ShortPathRequests.swap(shortRequests);
|
||||
moveCount = (i32)shortRequests.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
shortRequests.resize(moveCount);
|
||||
copy(m_ShortPathRequests.begin(), m_ShortPathRequests.begin() + moveCount, shortRequests.begin());
|
||||
m_ShortPathRequests.erase(m_ShortPathRequests.begin(), m_ShortPathRequests.begin() + moveCount);
|
||||
}
|
||||
|
||||
ProcessShortRequests(shortRequests);
|
||||
|
||||
m_SameTurnMovesCount = (u16)(m_SameTurnMovesCount + moveCount);
|
||||
amount = std::min(amount, from.size()); // Since we are rounding up before, ensure we aren't pushing beyond the end.
|
||||
worker.PushRequests(from, amount);
|
||||
from.erase(from.end() - amount, from.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,33 @@ class AtlasOverlay;
|
|||
*/
|
||||
class CCmpPathfinder final : public ICmpPathfinder
|
||||
{
|
||||
protected:
|
||||
|
||||
class PathfinderWorker
|
||||
{
|
||||
friend CCmpPathfinder;
|
||||
public:
|
||||
PathfinderWorker();
|
||||
|
||||
// Process path requests, checking if we should stop before each new one.
|
||||
void Work(const CCmpPathfinder& pathfinder);
|
||||
|
||||
private:
|
||||
// Insert requests in m_[Long/Short]Requests depending on from.
|
||||
// This could be removed when we may use if-constexpr in CCmpPathfinder::PushRequestsToWorkers
|
||||
template<typename T>
|
||||
void PushRequests(std::vector<T>& from, ssize_t amount);
|
||||
|
||||
// Stores our results, the main thread will fetch this.
|
||||
std::vector<PathResult> m_Results;
|
||||
|
||||
std::vector<LongPathRequest> m_LongRequests;
|
||||
std::vector<ShortPathRequest> m_ShortRequests;
|
||||
};
|
||||
|
||||
// Allow the workers to access our private variables
|
||||
friend class PathfinderWorker;
|
||||
|
||||
public:
|
||||
static void ClassInit(CComponentManager& componentManager)
|
||||
{
|
||||
|
|
@ -81,8 +108,8 @@ public:
|
|||
|
||||
std::vector<LongPathRequest> m_LongPathRequests;
|
||||
std::vector<ShortPathRequest> m_ShortPathRequests;
|
||||
u32 m_NextAsyncTicket; // unique IDs for asynchronous path requests
|
||||
u16 m_SameTurnMovesCount; // current number of same turn moves we have processed this turn
|
||||
u32 m_NextAsyncTicket; // Unique IDs for asynchronous path requests.
|
||||
u16 m_MaxSameTurnMoves; // Compute only this many paths when useMax is true in StartProcessingMoves.
|
||||
|
||||
// Lazily-constructed dynamic state (not serialized):
|
||||
|
||||
|
|
@ -101,9 +128,8 @@ public:
|
|||
std::unique_ptr<HierarchicalPathfinder> m_PathfinderHier;
|
||||
std::unique_ptr<LongPathfinder> m_LongPathfinder;
|
||||
|
||||
// For responsiveness we will process some moves in the same turn they were generated in
|
||||
|
||||
u16 m_MaxSameTurnMoves; // max number of moves that can be created and processed in the same turn
|
||||
// Workers process pathing requests.
|
||||
std::vector<PathfinderWorker> m_Workers;
|
||||
|
||||
AtlasOverlay* m_AtlasOverlay;
|
||||
|
||||
|
|
@ -168,11 +194,11 @@ public:
|
|||
|
||||
virtual Grid<u16> ComputeShoreGrid(bool expandOnWater = false);
|
||||
|
||||
virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) const;
|
||||
virtual void ComputePathImmediate(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) const;
|
||||
|
||||
virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, entity_id_t notify);
|
||||
|
||||
virtual WaypointPath ComputeShortPath(const ShortPathRequest& request) const;
|
||||
virtual WaypointPath ComputeShortPathImmediate(const ShortPathRequest& request) const;
|
||||
|
||||
virtual u32 ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t clearance, entity_pos_t range, const PathGoal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t controller, entity_id_t notify);
|
||||
|
||||
|
|
@ -194,13 +220,15 @@ public:
|
|||
|
||||
virtual ICmpObstruction::EFoundationCheck CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass, bool onlyCenterPoint) const;
|
||||
|
||||
virtual void FinishAsyncRequests();
|
||||
virtual void FetchAsyncResultsAndSendMessages();
|
||||
|
||||
void ProcessLongRequests(const std::vector<LongPathRequest>& longRequests);
|
||||
virtual void StartProcessingMoves(bool useMax);
|
||||
|
||||
void ProcessShortRequests(const std::vector<ShortPathRequest>& shortRequests);
|
||||
template <typename T>
|
||||
std::vector<T> PopMovesToProcess(std::vector<T>& requests, bool useMax = false, size_t maxMoves = 0);
|
||||
|
||||
virtual void ProcessSameTurnMoves();
|
||||
template <typename T>
|
||||
void PushRequestsToWorkers(std::vector<T>& from);
|
||||
|
||||
/**
|
||||
* Regenerates the grid based on the current obstruction list, if necessary
|
||||
|
|
|
|||
|
|
@ -686,7 +686,7 @@ void CCmpRallyPointRenderer::RecomputeRallyPointPath(size_t index, CmpPtr<ICmpPo
|
|||
start.X = m_RallyPoints[index-1].X;
|
||||
start.Y = m_RallyPoints[index-1].Y;
|
||||
}
|
||||
cmpPathfinder->ComputePath(start.X, start.Y, goal, cmpPathfinder->GetPassabilityClass(m_LinePassabilityClass), path);
|
||||
cmpPathfinder->ComputePathImmediate(start.X, start.Y, goal, cmpPathfinder->GetPassabilityClass(m_LinePassabilityClass), path);
|
||||
|
||||
// Check if we got a path back; if not we probably have two markers less than one tile apart.
|
||||
if (path.m_Waypoints.size() < 2)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,19 @@ class IObstructionTestFilter;
|
|||
|
||||
template<typename T> class Grid;
|
||||
|
||||
// Returned by asynchronous workers, used to send messages in the main thread.
|
||||
struct WaypointPath;
|
||||
|
||||
struct PathResult
|
||||
{
|
||||
PathResult() = default;
|
||||
PathResult(u32 t, entity_id_t n, WaypointPath p) : ticket(t), notify(n), path(p) {};
|
||||
|
||||
u32 ticket;
|
||||
entity_id_t notify;
|
||||
WaypointPath path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pathfinder algorithms.
|
||||
*
|
||||
|
|
@ -88,41 +101,37 @@ public:
|
|||
*/
|
||||
virtual Grid<u16> ComputeShoreGrid(bool expandOnWater = false) = 0;
|
||||
|
||||
/**
|
||||
* Compute a tile-based path from the given point to the goal, and return the set of waypoints.
|
||||
* The waypoints correspond to the centers of horizontally/vertically adjacent tiles
|
||||
* along the path.
|
||||
*/
|
||||
virtual void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) const = 0;
|
||||
|
||||
/**
|
||||
* Asynchronous version of ComputePath.
|
||||
* Request a long path computation, asynchronously.
|
||||
* The result will be sent as CMessagePathResult to 'notify'.
|
||||
* Returns a unique non-zero number, which will match the 'ticket' in the result,
|
||||
* so callers can recognise each individual request they make.
|
||||
*/
|
||||
virtual u32 ComputePathAsync(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, entity_id_t notify) = 0;
|
||||
|
||||
/**
|
||||
* If the debug overlay is enabled, render the path that will computed by ComputePath.
|
||||
/*
|
||||
* Request a long-path computation immediately
|
||||
*/
|
||||
virtual void SetDebugPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass) = 0;
|
||||
virtual void ComputePathImmediate(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) const = 0;
|
||||
|
||||
/**
|
||||
* Compute a precise path from the given point to the goal, and return the set of waypoints.
|
||||
* The path is based on the full set of obstructions that pass the filter, such that
|
||||
* a unit of clearance 'clearance' will be able to follow the path with no collisions.
|
||||
* The path is restricted to a box of radius 'range' from the starting point.
|
||||
*/
|
||||
virtual WaypointPath ComputeShortPath(const ShortPathRequest& request) const = 0;
|
||||
|
||||
/**
|
||||
* Asynchronous version of ComputeShortPath (using ControlGroupObstructionFilter).
|
||||
* Request a short path computation, asynchronously.
|
||||
* The result will be sent as CMessagePathResult to 'notify'.
|
||||
* Returns a unique non-zero number, which will match the 'ticket' in the result,
|
||||
* so callers can recognise each individual request they make.
|
||||
*/
|
||||
virtual u32 ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t clearance, entity_pos_t range, const PathGoal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t group, entity_id_t notify) = 0;
|
||||
virtual u32 ComputeShortPathAsync(entity_pos_t x0, entity_pos_t z0, entity_pos_t clearance, entity_pos_t range, const PathGoal& goal, pass_class_t passClass, bool avoidMovingUnits, entity_id_t controller, entity_id_t notify) = 0;
|
||||
|
||||
/*
|
||||
* Request a short-path computation immediately.
|
||||
*/
|
||||
virtual WaypointPath ComputeShortPathImmediate(const ShortPathRequest& request) const = 0;
|
||||
|
||||
/**
|
||||
* If the debug overlay is enabled, render the path that will computed by ComputePath.
|
||||
*/
|
||||
virtual void SetDebugPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass) = 0;
|
||||
|
||||
/**
|
||||
* Check whether the given movement line is valid and doesn't hit any obstructions
|
||||
|
|
@ -171,12 +180,12 @@ public:
|
|||
/**
|
||||
* Finish computing asynchronous path requests and send the CMessagePathResult messages.
|
||||
*/
|
||||
virtual void FinishAsyncRequests() = 0;
|
||||
virtual void FetchAsyncResultsAndSendMessages() = 0;
|
||||
|
||||
/**
|
||||
* Process moves during the same turn they were created in to improve responsiveness.
|
||||
* Tell asynchronous pathfinder threads that they can begin computing paths.
|
||||
*/
|
||||
virtual void ProcessSameTurnMoves() = 0;
|
||||
virtual void StartProcessingMoves(bool useMax) = 0;
|
||||
|
||||
/**
|
||||
* Regenerates the grid based on the current obstruction list, if necessary
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ public:
|
|||
PathGoal goal = { PathGoal::POINT, x1, z1 };
|
||||
|
||||
WaypointPath path;
|
||||
cmp->ComputePath(x0, z0, goal, cmp->GetPassabilityClass("default"), path);
|
||||
cmp->ComputePathImmediate(x0, z0, goal, cmp->GetPassabilityClass("default"), path);
|
||||
}
|
||||
|
||||
t = timer_Time() - t;
|
||||
|
|
@ -214,7 +214,7 @@ public:
|
|||
}
|
||||
|
||||
PathGoal goal = { PathGoal::POINT, range, range };
|
||||
WaypointPath path = cmpPathfinder->ComputeShortPath(ShortPathRequest{ 0, range/3, range/3, fixed::FromInt(2), range, goal, 0, false, 0, 0 });
|
||||
WaypointPath path = cmpPathfinder->ComputeShortPathImmediate(ShortPathRequest{ 0, range/3, range/3, fixed::FromInt(2), range, goal, 0, false, 0, 0 });
|
||||
for (size_t i = 0; i < path.m_Waypoints.size(); ++i)
|
||||
printf("# %d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToFloat(), path.m_Waypoints[i].z.ToFloat());
|
||||
}
|
||||
|
|
@ -369,7 +369,7 @@ public:
|
|||
PathGoal goal = { PathGoal::POINT, x1, z1 };
|
||||
|
||||
WaypointPath path;
|
||||
cmpPathfinder->ComputePath(x0, z0, goal, cmpPathfinder->GetPassabilityClass("default"), path);
|
||||
cmpPathfinder->ComputePathImmediate(x0, z0, goal, cmpPathfinder->GetPassabilityClass("default"), path);
|
||||
|
||||
u32 debugSteps;
|
||||
double debugTime;
|
||||
|
|
@ -418,7 +418,7 @@ public:
|
|||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
WaypointPath path;
|
||||
cmpPathfinder->ComputePath(x0, z0, goal, cmpPathfinder->GetPassabilityClass("default"), path);
|
||||
cmpPathfinder->ComputePathImmediate(x0, z0, goal, cmpPathfinder->GetPassabilityClass("default"), path);
|
||||
}
|
||||
t = timer_Time() - t;
|
||||
debug_printf("### RepeatPath %fms each (%fs total)\n", 1000*t / n, t);
|
||||
|
|
|
|||
Loading…
Reference in a new issue