Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpPathfinder.cpp
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode)) | ||||
m_TerrainOnlyGrid = NULL; | m_TerrainOnlyGrid = NULL; | ||||
FlushAIPathfinderDirtinessInformation(); | FlushAIPathfinderDirtinessInformation(); | ||||
m_NextAsyncTicket = 1; | m_NextAsyncTicket = 1; | ||||
m_AtlasOverlay = NULL; | m_AtlasOverlay = NULL; | ||||
m_VertexPathfinder = std::make_unique<VertexPathfinder>(m_GridSize, m_TerrainOnlyGrid); | size_t workerThreads = Threading::TaskManager::Instance().GetNumberOfWorkers(); | ||||
// Store one vertex pathfinder for each thread (including the main thread). | |||||
while (m_VertexPathfinders.size() < workerThreads + 1) | |||||
m_VertexPathfinders.emplace_back(m_GridSize, m_TerrainOnlyGrid); | |||||
m_LongPathfinder = std::make_unique<LongPathfinder>(); | m_LongPathfinder = std::make_unique<LongPathfinder>(); | ||||
m_PathfinderHier = std::make_unique<HierarchicalPathfinder>(); | m_PathfinderHier = std::make_unique<HierarchicalPathfinder>(); | ||||
// Set up one future for each worker thread. | |||||
m_Futures.resize(workerThreads); | |||||
// Register Relax NG validator | // Register Relax NG validator | ||||
CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng"); | CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng"); | ||||
// Since this is used as a system component (not loaded from an entity template), | // Since this is used as a system component (not loaded from an entity template), | ||||
// we can't use the real paramNode (it won't get handled properly when deserializing), | // we can't use the real paramNode (it won't get handled properly when deserializing), | ||||
// so load the data from a special XML file. | // so load the data from a special XML file. | ||||
CParamNode externalParamNode; | CParamNode externalParamNode; | ||||
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder"); | CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder"); | ||||
// Paths are computed: | // Paths are computed: | ||||
// - Before MT_Update | // - Before MT_Update | ||||
// - Before MT_MotionUnitFormation | // - Before MT_MotionUnitFormation | ||||
// - 'in-between' turns (effectively at the start until threading is implemented). | // - asynchronously between turn end and turn start. | ||||
// The latter of these must compute all outstanding requests, but the former two are capped | // The latter of these must compute all outstanding requests, but the former two are capped | ||||
// to avoid spending too much time there (since the latter are designed to be threaded and thus not block the GUI). | // to avoid spending too much time there (since the latter are threaded and thus much 'cheaper'). | ||||
// This loads that maximum number (note that it's per computation call, not per turn for now). | // This loads that maximum number (note that it's per computation call, not per turn for now). | ||||
const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder"); | const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder"); | ||||
m_MaxSameTurnMoves = (u16)pathingSettings.GetChild("MaxSameTurnMoves").ToInt(); | m_MaxSameTurnMoves = (u16)pathingSettings.GetChild("MaxSameTurnMoves").ToInt(); | ||||
const CParamNode::ChildrenMap& passClasses = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses").GetChildren(); | const CParamNode::ChildrenMap& passClasses = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses").GetChildren(); | ||||
for (CParamNode::ChildrenMap::const_iterator it = passClasses.begin(); it != passClasses.end(); ++it) | for (CParamNode::ChildrenMap::const_iterator it = passClasses.begin(); it != passClasses.end(); ++it) | ||||
{ | { | ||||
std::string name = it->first; | std::string name = it->first; | ||||
ENSURE((int)m_PassClasses.size() <= PASS_CLASS_BITS); | ENSURE((int)m_PassClasses.size() <= PASS_CLASS_BITS); | ||||
pass_class_t mask = PASS_CLASS_MASK_FROM_INDEX(m_PassClasses.size()); | pass_class_t mask = PASS_CLASS_MASK_FROM_INDEX(m_PassClasses.size()); | ||||
m_PassClasses.push_back(PathfinderPassability(mask, it->second)); | m_PassClasses.push_back(PathfinderPassability(mask, it->second)); | ||||
m_PassClassMasks[name] = mask; | m_PassClassMasks[name] = mask; | ||||
} | } | ||||
} | } | ||||
CCmpPathfinder::~CCmpPathfinder() {}; | CCmpPathfinder::~CCmpPathfinder() {}; | ||||
void CCmpPathfinder::Deinit() | void CCmpPathfinder::Deinit() | ||||
{ | { | ||||
SetDebugOverlay(false); // cleans up memory | SetDebugOverlay(false); // cleans up memory | ||||
// Wait on all pathfinding tasks. | |||||
for (Future<void>& future : m_Futures) | |||||
future.Cancel(); | |||||
m_Futures.clear(); | |||||
SAFE_DELETE(m_AtlasOverlay); | SAFE_DELETE(m_AtlasOverlay); | ||||
SAFE_DELETE(m_Grid); | SAFE_DELETE(m_Grid); | ||||
SAFE_DELETE(m_TerrainOnlyGrid); | SAFE_DELETE(m_TerrainOnlyGrid); | ||||
} | } | ||||
template<> | template<> | ||||
struct SerializeHelper<LongPathRequest> | struct SerializeHelper<LongPathRequest> | ||||
▲ Show 20 Lines • Show All 634 Lines • ▼ Show 20 Lines | |||||
void CCmpPathfinder::ComputePathImmediate(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass, WaypointPath& ret) 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); | m_LongPathfinder->ComputePath(*m_PathfinderHier, x0, z0, goal, passClass, ret); | ||||
} | } | ||||
WaypointPath CCmpPathfinder::ComputeShortPathImmediate(const ShortPathRequest& request) const | WaypointPath CCmpPathfinder::ComputeShortPathImmediate(const ShortPathRequest& request) const | ||||
{ | { | ||||
return m_VertexPathfinder->ComputeShortPath(request, CmpPtr<ICmpObstructionManager>(GetSystemEntity())); | return m_VertexPathfinders.front().ComputeShortPath(request, CmpPtr<ICmpObstructionManager>(GetSystemEntity())); | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
template<typename U> | template<typename U> | ||||
void CCmpPathfinder::PathRequests<T>::Compute(const CCmpPathfinder& cmpPathfinder, const U& pathfinder) | void CCmpPathfinder::PathRequests<T>::Compute(const CCmpPathfinder& cmpPathfinder, const U& pathfinder) | ||||
{ | { | ||||
static_assert((std::is_same_v<T, LongPathRequest> && std::is_same_v<U, LongPathfinder>) || | static_assert((std::is_same_v<T, LongPathRequest> && std::is_same_v<U, LongPathfinder>) || | ||||
(std::is_same_v<T, ShortPathRequest> && std::is_same_v<U, VertexPathfinder>)); | (std::is_same_v<T, ShortPathRequest> && std::is_same_v<U, VertexPathfinder>)); | ||||
Show All 19 Lines | |||||
} | } | ||||
void CCmpPathfinder::SendRequestedPaths() | void CCmpPathfinder::SendRequestedPaths() | ||||
{ | { | ||||
PROFILE2("SendRequestedPaths"); | PROFILE2("SendRequestedPaths"); | ||||
if (!m_LongPathRequests.m_ComputeDone || !m_ShortPathRequests.m_ComputeDone) | if (!m_LongPathRequests.m_ComputeDone || !m_ShortPathRequests.m_ComputeDone) | ||||
{ | { | ||||
m_ShortPathRequests.Compute(*this, *m_VertexPathfinder); | // Also start computing on the main thread to finish faster. | ||||
m_ShortPathRequests.Compute(*this, m_VertexPathfinders.front()); | |||||
m_LongPathRequests.Compute(*this, *m_LongPathfinder); | m_LongPathRequests.Compute(*this, *m_LongPathfinder); | ||||
} | } | ||||
// We're done, clear futures. | |||||
// Use CancelOrWait instead of just Cancel to ensure determinism. | |||||
for (Future<void>& future : m_Futures) | |||||
future.CancelOrWait(); | |||||
{ | { | ||||
PROFILE2("PostMessages"); | PROFILE2("PostMessages"); | ||||
for (PathResult& path : m_ShortPathRequests.m_Results) | for (PathResult& path : m_ShortPathRequests.m_Results) | ||||
{ | { | ||||
CMessagePathResult msg(path.ticket, path.path); | CMessagePathResult msg(path.ticket, path.path); | ||||
GetSimContext().GetComponentManager().PostMessage(path.notify, msg); | GetSimContext().GetComponentManager().PostMessage(path.notify, msg); | ||||
} | } | ||||
for (PathResult& path : m_LongPathRequests.m_Results) | for (PathResult& path : m_LongPathRequests.m_Results) | ||||
{ | { | ||||
CMessagePathResult msg(path.ticket, path.path); | CMessagePathResult msg(path.ticket, path.path); | ||||
GetSimContext().GetComponentManager().PostMessage(path.notify, msg); | GetSimContext().GetComponentManager().PostMessage(path.notify, msg); | ||||
} | } | ||||
} | } | ||||
m_ShortPathRequests.ClearComputed(); | m_ShortPathRequests.ClearComputed(); | ||||
m_LongPathRequests.ClearComputed(); | m_LongPathRequests.ClearComputed(); | ||||
} | } | ||||
void CCmpPathfinder::StartProcessingMoves(bool useMax) | void CCmpPathfinder::StartProcessingMoves(bool useMax) | ||||
{ | { | ||||
m_ShortPathRequests.PrepareForComputation(useMax ? m_MaxSameTurnMoves : 0); | m_ShortPathRequests.PrepareForComputation(useMax ? m_MaxSameTurnMoves : 0); | ||||
m_LongPathRequests.PrepareForComputation(useMax ? m_MaxSameTurnMoves : 0); | m_LongPathRequests.PrepareForComputation(useMax ? m_MaxSameTurnMoves : 0); | ||||
Threading::TaskManager& taskManager = Threading::TaskManager::Instance(); | |||||
for (size_t i = 0; i < m_Futures.size(); ++i) | |||||
{ | |||||
ENSURE(!m_Futures[i].Valid()); | |||||
// Pass the i+1th vertex pathfinder to keep the first for the main thread, | |||||
// each thread get its own instance to avoid conflicts in cached data. | |||||
m_Futures[i] = taskManager.PushTask([&pathfinder=*this, &vertexPfr=m_VertexPathfinders[i + 1]]() { | |||||
PROFILE2("Async pathfinding"); | |||||
pathfinder.m_ShortPathRequests.Compute(pathfinder, vertexPfr); | |||||
pathfinder.m_LongPathRequests.Compute(pathfinder, *pathfinder.m_LongPathfinder); | |||||
}); | |||||
} | } | ||||
} | |||||
////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////// | ||||
bool CCmpPathfinder::IsGoalReachable(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass) | bool CCmpPathfinder::IsGoalReachable(entity_pos_t x0, entity_pos_t z0, const PathGoal& goal, pass_class_t passClass) | ||||
{ | { | ||||
PROFILE2("IsGoalReachable"); | PROFILE2("IsGoalReachable"); | ||||
u16 i, j; | u16 i, j; | ||||
▲ Show 20 Lines • Show All 102 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator