- attempt to improve ai decision performance by having each ai player updates done in a thread
This commit is contained in:
parent
46869536bb
commit
ee2ad95164
|
@ -32,9 +32,198 @@ using namespace Shared::Graphics;
|
|||
|
||||
namespace Glest{ namespace Game{
|
||||
|
||||
AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex, int useStartLocation) : fp(NULL) {
|
||||
// =====================================================
|
||||
// class FactionThread
|
||||
// =====================================================
|
||||
|
||||
AiInterfaceThread::AiInterfaceThread(AiInterface *aiIntf) : BaseThread() {
|
||||
this->triggerIdMutex = new Mutex();
|
||||
this->aiIntf = aiIntf;
|
||||
}
|
||||
|
||||
AiInterfaceThread::~AiInterfaceThread() {
|
||||
delete triggerIdMutex;
|
||||
triggerIdMutex = NULL;
|
||||
}
|
||||
|
||||
void AiInterfaceThread::setQuitStatus(bool value) {
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s] Line: %d value = %d\n",__FILE__,__FUNCTION__,__LINE__,value);
|
||||
|
||||
BaseThread::setQuitStatus(value);
|
||||
if(value == true) {
|
||||
signal(-1);
|
||||
}
|
||||
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
}
|
||||
|
||||
void AiInterfaceThread::signal(int frameIndex) {
|
||||
if(frameIndex >= 0) {
|
||||
static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__);
|
||||
MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId);
|
||||
this->frameIndex.first = frameIndex;
|
||||
this->frameIndex.second = false;
|
||||
|
||||
safeMutex.ReleaseLock();
|
||||
}
|
||||
semTaskSignalled.signal();
|
||||
}
|
||||
|
||||
void AiInterfaceThread::setTaskCompleted(int frameIndex) {
|
||||
if(frameIndex >= 0) {
|
||||
static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__);
|
||||
MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId);
|
||||
if(this->frameIndex.first == frameIndex) {
|
||||
this->frameIndex.second = true;
|
||||
}
|
||||
safeMutex.ReleaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
bool AiInterfaceThread::canShutdown(bool deleteSelfIfShutdownDelayed) {
|
||||
bool ret = (getExecutingTask() == false);
|
||||
if(ret == false && deleteSelfIfShutdownDelayed == true) {
|
||||
setDeleteSelfOnExecutionDone(deleteSelfIfShutdownDelayed);
|
||||
signalQuit();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AiInterfaceThread::isSignalCompleted(int frameIndex) {
|
||||
if(getRunningStatus() == false) {
|
||||
return true;
|
||||
}
|
||||
static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__);
|
||||
MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId);
|
||||
//bool result = (event != NULL ? event->eventCompleted : true);
|
||||
bool result = (this->frameIndex.first == frameIndex && this->frameIndex.second == true);
|
||||
|
||||
//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] worker thread this = %p, this->frameIndex.first = %d, this->frameIndex.second = %d\n",__FILE__,__FUNCTION__,__LINE__,this,this->frameIndex.first,this->frameIndex.second);
|
||||
|
||||
safeMutex.ReleaseLock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void AiInterfaceThread::execute() {
|
||||
RunningStatusSafeWrapper runningStatus(this);
|
||||
try {
|
||||
//setRunningStatus(true);
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] ****************** STARTING worker thread this = %p\n",__FILE__,__FUNCTION__,__LINE__,this);
|
||||
|
||||
bool minorDebugPerformance = false;
|
||||
Chrono chrono;
|
||||
|
||||
//unsigned int idx = 0;
|
||||
for(;this->aiIntf != NULL;) {
|
||||
if(getQuitStatus() == true) {
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
break;
|
||||
}
|
||||
|
||||
semTaskSignalled.waitTillSignalled();
|
||||
|
||||
if(getQuitStatus() == true) {
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
break;
|
||||
}
|
||||
|
||||
static string mutexOwnerId = string(__FILE__) + string("_") + intToStr(__LINE__);
|
||||
MutexSafeWrapper safeMutex(triggerIdMutex,mutexOwnerId);
|
||||
bool executeTask = (frameIndex.first >= 0);
|
||||
|
||||
//if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] frameIndex = %d this = %p executeTask = %d\n",__FILE__,__FUNCTION__,__LINE__,frameIndex.first, this, executeTask);
|
||||
|
||||
safeMutex.ReleaseLock();
|
||||
|
||||
if(executeTask == true) {
|
||||
ExecutingTaskSafeWrapper safeExecutingTaskMutex(this);
|
||||
|
||||
MutexSafeWrapper safeMutex(this->aiIntf->getMutex(),string(__FILE__) + "_" + intToStr(__LINE__));
|
||||
|
||||
this->aiIntf->update();
|
||||
|
||||
/*
|
||||
World *world = faction->getWorld();
|
||||
|
||||
//Config &config= Config::getInstance();
|
||||
//bool sortedUnitsAllowed = config.getBool("AllowGroupedUnitCommands","true");
|
||||
bool sortedUnitsAllowed = false;
|
||||
if(sortedUnitsAllowed) {
|
||||
faction->sortUnitsByCommandGroups();
|
||||
}
|
||||
|
||||
MutexSafeWrapper safeMutex(faction->getUnitMutex(),string(__FILE__) + "_" + intToStr(__LINE__));
|
||||
|
||||
//if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
|
||||
if(minorDebugPerformance) chrono.start();
|
||||
|
||||
int unitCount = faction->getUnitCount();
|
||||
for(int j = 0; j < unitCount; ++j) {
|
||||
Unit *unit = faction->getUnit(j);
|
||||
if(unit == NULL) {
|
||||
throw megaglest_runtime_error("unit == NULL");
|
||||
}
|
||||
|
||||
int64 elapsed1 = 0;
|
||||
if(minorDebugPerformance) elapsed1 = chrono.getMillis();
|
||||
|
||||
bool update = unit->needToUpdate();
|
||||
|
||||
if(minorDebugPerformance && (chrono.getMillis() - elapsed1) >= 1) printf("Faction [%d - %s] #1-unit threaded updates on frame: %d for [%d] unit # %d, unitCount = %d, took [%lld] msecs\n",faction->getStartLocationIndex(),faction->getType()->getName().c_str(),frameIndex.first,faction->getUnitPathfindingListCount(),j,unitCount,(long long int)chrono.getMillis() - elapsed1);
|
||||
|
||||
//update = true;
|
||||
if(update == true) {
|
||||
|
||||
int64 elapsed2 = 0;
|
||||
if(minorDebugPerformance) elapsed2 = chrono.getMillis();
|
||||
|
||||
world->getUnitUpdater()->updateUnitCommand(unit,frameIndex.first);
|
||||
|
||||
if(minorDebugPerformance && (chrono.getMillis() - elapsed2) >= 1) printf("Faction [%d - %s] #2-unit threaded updates on frame: %d for [%d] unit # %d, unitCount = %d, took [%lld] msecs\n",faction->getStartLocationIndex(),faction->getType()->getName().c_str(),frameIndex.first,faction->getUnitPathfindingListCount(),j,unitCount,(long long int)chrono.getMillis() - elapsed2);
|
||||
}
|
||||
}
|
||||
|
||||
if(minorDebugPerformance && chrono.getMillis() >= 1) printf("Faction [%d - %s] threaded updates on frame: %d for [%d] units took [%lld] msecs\n",faction->getStartLocationIndex(),faction->getType()->getName().c_str(),frameIndex.first,faction->getUnitPathfindingListCount(),(long long int)chrono.getMillis());
|
||||
*/
|
||||
|
||||
safeMutex.ReleaseLock();
|
||||
|
||||
setTaskCompleted(frameIndex.first);
|
||||
}
|
||||
|
||||
if(getQuitStatus() == true) {
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d] ****************** ENDING worker thread this = %p\n",__FILE__,__FUNCTION__,__LINE__,this);
|
||||
}
|
||||
catch(const exception &ex) {
|
||||
//setRunningStatus(false);
|
||||
|
||||
SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",__FILE__,__FUNCTION__,__LINE__,ex.what());
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
|
||||
throw megaglest_runtime_error(ex.what());
|
||||
}
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex,
|
||||
int useStartLocation) : fp(NULL) {
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
|
||||
this->aiMutex = new Mutex();
|
||||
this->workerThread = NULL;
|
||||
this->world= game.getWorld();
|
||||
this->commander= game.getCommander();
|
||||
this->console= game.getConsole();
|
||||
|
@ -75,6 +264,21 @@ AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex, int useSta
|
|||
}
|
||||
fprintf(fp, "MegaGlest AI log file for Tech [%s] Faction [%s] #%d\n\n",this->gameSettings->getTech().c_str(),this->world->getFaction(this->factionIndex)->getType()->getName().c_str(),this->factionIndex);
|
||||
}
|
||||
|
||||
|
||||
if( Config::getInstance().getBool("EnableAIWorkerThreads","true") == true) {
|
||||
if(workerThread != NULL) {
|
||||
workerThread->signalQuit();
|
||||
if(workerThread->shutdownAndWait() == true) {
|
||||
delete workerThread;
|
||||
}
|
||||
workerThread = NULL;
|
||||
}
|
||||
this->workerThread = new AiInterfaceThread(this);
|
||||
this->workerThread->setUniqueID(__FILE__);
|
||||
this->workerThread->start();
|
||||
}
|
||||
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
}
|
||||
|
||||
|
@ -82,11 +286,39 @@ AiInterface::~AiInterface() {
|
|||
if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI factionIndex = %d, teamIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,this->factionIndex,this->teamIndex);
|
||||
cacheUnitHarvestResourceLookup.clear();
|
||||
|
||||
if(workerThread != NULL) {
|
||||
workerThread->signalQuit();
|
||||
if(workerThread->shutdownAndWait() == true) {
|
||||
delete workerThread;
|
||||
}
|
||||
workerThread = NULL;
|
||||
}
|
||||
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
}
|
||||
|
||||
delete aiMutex;
|
||||
aiMutex = NULL;
|
||||
}
|
||||
|
||||
void AiInterface::signalWorkerThread(int frameIndex) {
|
||||
if(workerThread != NULL) {
|
||||
workerThread->signal(frameIndex);
|
||||
}
|
||||
else {
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
|
||||
bool AiInterface::isWorkerThreadSignalCompleted(int frameIndex) {
|
||||
if(workerThread != NULL) {
|
||||
return workerThread->isSignalCompleted(frameIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ==================== main ====================
|
||||
|
||||
void AiInterface::update() {
|
||||
|
@ -100,6 +332,7 @@ void AiInterface::printLog(int logLevel, const string &s){
|
|||
if(this->logLevel >= logLevel) {
|
||||
string logString= "(" + intToStr(factionIndex) + ") " + s;
|
||||
|
||||
MutexSafeWrapper safeMutex(aiMutex,string(__FILE__) + "_" + intToStr(__LINE__));
|
||||
//print log to file
|
||||
if(fp != NULL) {
|
||||
fprintf(fp, "%s\n", logString.c_str());
|
||||
|
|
|
@ -31,6 +31,26 @@ namespace Glest{ namespace Game{
|
|||
/// The AI will interact with the game through this interface
|
||||
// =====================================================
|
||||
|
||||
class AiInterfaceThread : public BaseThread {
|
||||
protected:
|
||||
|
||||
AiInterface *aiIntf;
|
||||
Semaphore semTaskSignalled;
|
||||
Mutex *triggerIdMutex;
|
||||
std::pair<int,bool> frameIndex;
|
||||
|
||||
virtual void setQuitStatus(bool value);
|
||||
virtual void setTaskCompleted(int frameIndex);
|
||||
virtual bool canShutdown(bool deleteSelfIfShutdownDelayed=false);
|
||||
|
||||
public:
|
||||
AiInterfaceThread(AiInterface *aiIntf);
|
||||
virtual ~AiInterfaceThread();
|
||||
virtual void execute();
|
||||
void signal(int frameIndex);
|
||||
bool isSignalCompleted(int frameIndex);
|
||||
};
|
||||
|
||||
class AiInterface {
|
||||
private:
|
||||
World *world;
|
||||
|
@ -52,6 +72,10 @@ private:
|
|||
|
||||
std::map<const ResourceType *,int> cacheUnitHarvestResourceLookup;
|
||||
|
||||
Mutex *aiMutex;
|
||||
|
||||
AiInterfaceThread *workerThread;
|
||||
|
||||
public:
|
||||
AiInterface(Game &game, int factionIndex, int teamIndex, int useStartLocation=-1);
|
||||
~AiInterface();
|
||||
|
@ -59,6 +83,11 @@ public:
|
|||
//main
|
||||
void update();
|
||||
|
||||
inline Mutex * getMutex() {return aiMutex;}
|
||||
|
||||
void signalWorkerThread(int frameIndex);
|
||||
bool isWorkerThreadSignalCompleted(int frameIndex);
|
||||
|
||||
//get
|
||||
int getTimer() const {return timer;}
|
||||
int getFactionIndex() const {return factionIndex;}
|
||||
|
|
|
@ -83,6 +83,7 @@ private:
|
|||
World *world;
|
||||
Chrono perfTimer;
|
||||
|
||||
Mutex commandMutex;
|
||||
//CommanderNetworkThread *networkThread;
|
||||
//Game *game;
|
||||
std::vector<std::pair<int,NetworkCommand> > replayCommandList;
|
||||
|
|
|
@ -1476,6 +1476,9 @@ void Game::update() {
|
|||
chrono.start();
|
||||
//AiInterface
|
||||
if(commander.hasReplayCommandListForFrame() == false) {
|
||||
|
||||
|
||||
/*
|
||||
for(int j = 0; j < world.getFactionCount(); ++j) {
|
||||
Faction *faction = world.getFaction(j);
|
||||
|
||||
|
@ -1492,6 +1495,54 @@ void Game::update() {
|
|||
if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] [i = %d] faction = %d, factionCount = %d, took msecs: %lld [after AI updates]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,j,world.getFactionCount(),chrono.getMillis());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Signal the faction threads to do any pre-processing
|
||||
for(int j = 0; j < world.getFactionCount(); ++j) {
|
||||
Faction *faction = world.getFaction(j);
|
||||
|
||||
//printf("Faction Index = %d enableServerControlledAI = %d, isNetworkGame = %d, role = %d isCPU player = %d scriptManager.getPlayerModifiers(j)->getAiEnabled() = %d\n",j,enableServerControlledAI,isNetworkGame,role,faction->getCpuControl(enableServerControlledAI,isNetworkGame,role),scriptManager.getPlayerModifiers(j)->getAiEnabled());
|
||||
|
||||
if( faction->getCpuControl(enableServerControlledAI,isNetworkGame,role) == true &&
|
||||
scriptManager.getPlayerModifiers(j)->getAiEnabled() == true) {
|
||||
|
||||
if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] [i = %d] faction = %d, factionCount = %d, took msecs: %lld [before AI updates]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,i,j,world.getFactionCount(),chrono.getMillis());
|
||||
aiInterfaces[j]->signalWorkerThread(world.getFrameCount());
|
||||
}
|
||||
}
|
||||
|
||||
bool workThreadsFinished = false;
|
||||
Chrono chrono;
|
||||
chrono.start();
|
||||
|
||||
const int MAX_FACTION_THREAD_WAIT_MILLISECONDS = 20000;
|
||||
for(;chrono.getMillis() < MAX_FACTION_THREAD_WAIT_MILLISECONDS;) {
|
||||
workThreadsFinished = true;
|
||||
for(int j = 0; j < world.getFactionCount(); ++j) {
|
||||
Faction *faction = world.getFaction(j);
|
||||
if(faction == NULL) {
|
||||
throw megaglest_runtime_error("faction == NULL");
|
||||
}
|
||||
if( faction->getCpuControl(enableServerControlledAI,isNetworkGame,role) == true &&
|
||||
scriptManager.getPlayerModifiers(j)->getAiEnabled() == true) {
|
||||
if(aiInterfaces[j]->isWorkerThreadSignalCompleted(world.getFrameCount()) == false) {
|
||||
workThreadsFinished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(workThreadsFinished == false) {
|
||||
//sleep(0);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
// Simply show a progress message while replaying commands
|
||||
|
|
|
@ -946,7 +946,7 @@ void Renderer::swapBuffers() {
|
|||
return;
|
||||
}
|
||||
//glFlush(); // should not be required - http://www.opengl.org/wiki/Common_Mistakes
|
||||
glFlush();
|
||||
//glFlush();
|
||||
|
||||
GraphicsInterface::getInstance().getCurrentContext()->swapBuffers();
|
||||
}
|
||||
|
|
|
@ -226,14 +226,16 @@ GameNetworkInterface::GameNetworkInterface(){
|
|||
|
||||
void GameNetworkInterface::requestCommand(const NetworkCommand *networkCommand, bool insertAtStart) {
|
||||
assert(networkCommand != NULL);
|
||||
//Mutex *mutex = getServerSynchAccessor();
|
||||
Mutex *mutex = getServerSynchAccessor();
|
||||
|
||||
if(insertAtStart == false) {
|
||||
MutexSafeWrapper safeMutex(mutex,string(__FILE__) + "_" + intToStr(__LINE__));
|
||||
//if(mutex != NULL) mutex->p();
|
||||
requestedCommands.push_back(*networkCommand);
|
||||
//if(mutex != NULL) mutex->v();
|
||||
}
|
||||
else {
|
||||
MutexSafeWrapper safeMutex(mutex,string(__FILE__) + "_" + intToStr(__LINE__));
|
||||
//if(mutex != NULL) mutex->p();
|
||||
requestedCommands.insert(requestedCommands.begin(),*networkCommand);
|
||||
//if(mutex != NULL) mutex->v();
|
||||
|
|
Loading…
Reference in New Issue