41 #ifndef _framescheduler_cpp
42 #define _framescheduler_cpp
49 #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
63 #ifdef _MEZZ_THREAD_WIN32_
65 #pragma warning( disable : 4706) // Disable Legitimate assignment in a WorkUnit acquisition loops
77 SpinLock FrameScheduler::FrameSchedulersLock;
78 std::vector<FrameScheduler*> FrameScheduler::FrameSchedulers;
82 void TerminateHandler()
84 lock_guard<SpinLock> g(FrameScheduler::FrameSchedulersLock);
85 for(std::vector<FrameScheduler*>::iterator Iter = FrameScheduler::FrameSchedulers.begin();
86 Iter != FrameScheduler::FrameSchedulers.end();
89 FrameScheduler* CurrentFrameScheduler = *Iter;
90 CurrentFrameScheduler->ForceLogFlush();
97 void ThreadWork(
void* ThreadStorage)
99 DefaultThreadSpecificStorage::Type& Storage = *((DefaultThreadSpecificStorage::Type*)ThreadStorage);
100 FrameScheduler& FS = *(Storage.GetFrameScheduler());
101 iWorkUnit* CurrentUnit;
105 while( (CurrentUnit = FS.GetNextWorkUnit()) )
107 if(
Starting==CurrentUnit->TakeOwnerShip())
108 { CurrentUnit->operator()(Storage); }
110 }
while(!FS.AreAllWorkUnitsComplete());
115 void ThreadWorkAffinity(
void* ThreadStorage)
117 DefaultThreadSpecificStorage::Type& Storage = *((DefaultThreadSpecificStorage::Type*)ThreadStorage);
118 FrameScheduler& FS = *(Storage.GetFrameScheduler());
119 iWorkUnit* CurrentUnit;
122 while( (CurrentUnit = FS.GetNextWorkUnitAffinity()) )
124 if(
Starting==CurrentUnit->TakeOwnerShip())
125 { CurrentUnit->operator()(Storage); }
128 while(!FS.AreAllWorkUnitsComplete());
138 FrameSchedulers.push_back(SchedulerToAdd);
144 FrameSchedulers.erase( std::remove( FrameSchedulers.begin(), FrameSchedulers.end(), SchedulerToRemove ), FrameSchedulers.end() );
148 void FrameScheduler::CleanUpThreads()
149 { JoinAllThreads(); }
151 void FrameScheduler::DeleteThreads()
153 for(std::vector<Thread*>::iterator Iter = Threads.begin(); Iter!=Threads.end(); ++Iter)
157 void FrameScheduler::UpdateDependentGraph(
const std::vector<WorkUnitKey>& Units)
160 for(std::vector<WorkUnitKey>::const_iterator Iter=Units.begin(); Iter!=Units.end(); ++Iter)
163 size_t Max = Iter->Unit->GetImmediateDependencyCount();
164 for(
size_t Counter=0; Counter<Max; ++Counter)
165 { DependentGraph[Iter->Unit->GetDependency(Counter)].insert(Iter->Unit); }
169 void FrameScheduler::UpdateWorkUnitKeys(std::vector<WorkUnitKey> &Units)
171 for(std::vector<WorkUnitKey>::iterator Iter=Units.begin(); Iter!=Units.end(); ++Iter)
172 { *Iter = Iter->Unit->GetSortingKey(*
this); }
177 FrameScheduler::FrameScheduler(std::fstream *_LogDestination,
Whole StartingThreadCount) :
187 CurrentThreadCount(StartingThreadCount),
188 FrameCount(0), TargetFrameLength(16666),
189 TimingCostAllowance(0),
190 MainThreadID(this_thread::
get_id()),
191 LoggingToAnOwnedFileStream(true),
196 { InstallLog(_LogDestination); }
198 { InstallLog(
new std::fstream(
"Mezzanine.log", std::ios::out | std::ios::trunc)); }
214 CurrentThreadCount(StartingThreadCount),
215 FrameCount(0), TargetFrameLength(16666),
216 TimingCostAllowance(0),
217 MainThreadID(this_thread::
get_id()),
218 LoggingToAnOwnedFileStream(false),
222 InstallLog(_LogDestination);
229 std::set_terminate(TerminateHandler);
238 {
delete Iter->Unit; }
241 for(std::vector<DefaultThreadSpecificStorage::Type*>::iterator Iter =
Resources.begin(); Iter!=
Resources.end(); ++Iter)
252 (*this->
LogDestination) <<
"<WorkUnitMainInsertion ID=\"" << hex << MoreWork <<
"\" Name=\"" << WorkUnitName <<
"\" />" << endl;
259 (*this->
LogDestination) <<
"<WorkUnitAffinityInsertion ID=\"" << hex << MoreWork <<
"\" Name=\"" << WorkUnitName <<
"\" />" << endl;
266 (*this->
LogDestination) <<
"<WorkUnitMonopolyInsertion ID=\"" << hex << MoreWork <<
"\" Name=\"" << WorkUnitName <<
"\" />" << endl;
271 if(UpdateDependentGraph_)
282 if(UpdateDependentGraph_)
304 if(Iter->Unit == LessWork)
307 { RemovalTarget = Iter;}
309 { std::swap (*Iter,*(Iter+1)); }
311 Iter->Unit->RemoveDependency(LessWork);
319 { Iter->Unit->RemoveDependency(LessWork); }
330 if(Iter->Unit == LessWork)
333 { RemovalTarget = Iter;}
335 { std::swap (*Iter,*(Iter+1)); }
337 Iter->Unit->RemoveDependency(LessWork);
345 { Iter->Unit->RemoveDependency(LessWork); }
354 { Iter->Unit->RemoveDependency(LessWork); }
359 { Iter->Unit->RemoveDependency(LessWork); }
379 if(UsedCachedDepedentGraph)
392 #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
393 bool CompleteSoFar =
true;
394 Int32 CurrentRun = DecacheMain;
397 if (
NotStarted==Iter->Unit->GetRunningState())
399 if(Iter->Unit->IsEveryDependencyComplete())
400 {
return Iter->Unit; }
407 if(
Complete==Iter->Unit->GetRunningState())
414 if (
NotStarted==Iter->Unit->GetRunningState())
416 if(Iter->Unit->IsEveryDependencyComplete())
417 {
return Iter->Unit; }
427 #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
428 bool CompleteSoFar =
true;
429 Int32 CurrentRun = DecacheAffinity;
432 if (
NotStarted==Iter->Unit->GetRunningState())
434 if(Iter->Unit->IsEveryDependencyComplete())
435 {
return Iter->Unit; }
442 if(
Complete==Iter->Unit->GetRunningState())
449 if (
NotStarted==Iter->Unit->GetRunningState())
451 if(Iter->Unit->IsEveryDependencyComplete())
452 {
return Iter->Unit; }
465 if(
Complete!=Iter->Unit->GetRunningState())
471 if(
Complete!=Iter->Unit->GetRunningState())
542 { (*Iter)->operator()(*(
Resources.at(0))); }
561 for(std::vector<Thread*>::iterator Iter=
Threads.begin(); Iter!=
Threads.end(); ++Iter)
583 { Iter->Unit->PrepareForNextFrame(); }
585 { Iter->Unit->PrepareForNextFrame(); }
586 #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
595 Whole TargetFrameEnd=0;
629 std::vector<Resource*>::iterator Results =
Resources.begin();
633 for(std::vector<Thread*>::iterator Iter=
Threads.begin(); Iter!=
Threads.end(); ++Iter)
636 if ( (*Iter)->get_id()==ID)
660 Whole MainCount = Iter->Unit->GetImmediateDependencyCount();
661 for(
Whole Counter = 0; Counter<MainCount; Counter++)
664 "Unit=\"" << hex << Iter->Unit
665 <<
"\" DependsOn=\"" << Iter->Unit->GetDependency(Counter) <<
"\" "
672 Whole AffinityCount = Iter->Unit->GetImmediateDependencyCount();
673 for(
Whole Counter = 0; Counter<AffinityCount; Counter++)
676 "Unit=\"" << hex << Iter->Unit
677 <<
"\" DependsOn=\"" << Iter->Unit->GetDependency(Counter) <<
"\" "
684 Whole MonopolyCount = (*Iter)->GetImmediateDependencyCount();
685 for(
Whole Counter = 0; Counter<MonopolyCount; Counter++)
688 "Unit=\"" << hex << (*Iter)
689 <<
"\" DependsOn=\"" << (*Iter)->GetDependency(Counter) <<
"\" "
699 void FrameScheduler::ChangeLogTarget(std::ostream* LogTarget)
702 InstallLog(LogTarget);
705 void FrameScheduler::InstallLog(std::ostream* LogTarget)
708 (*LogDestination)<<
"<MezzanineLog>" << std::endl;
712 void FrameScheduler::RemoveLog()
714 (*LogDestination) <<
"</MezzanineLog>" << std::endl;
770 Resources[0]->SwapAllBufferedResources();
775 Resources[Count]->SwapAllBufferedResources();
int32_t Int32
An 32-bit integer.
virtual void AddWorkUnitMonopoly(MonopolyWorkUnit *MoreWork, const String &WorkUnitName)
Add a MonopolyWorkUnit for execution at the beginning of the frame.
Use this to get the default rolling average for a given type.
virtual void SetFrameRate(const Whole &FrameRate)
Set the desired Frate rate.
virtual Whole GetDependentCountOf(iWorkUnit *Work, bool UsedCachedDepedentGraph=false)
How many other WorkUnit instances must wait on this one.
ThreadId MEZZ_LIB get_id()
Return the thread ID of the calling thread.
virtual void DoOneFrame()
Do one frame worth of work.
std::stringstream Logger
In case we ever replace the stringstream with another class, this will allow us to swap it out...
Resource * GetThreadResource(ThreadId ID=this_thread::get_id())
Get the Resource to go with a thread of a given ID.
bool Boole
Generally acts a single bit, true or false.
virtual void SortWorkUnitsMain(bool UpdateDependentGraph_=true)
Sort the the main pool of WorkUnits to allow them to be used more efficiently in the next frame execu...
std::vector< WorkUnitKey > WorkUnitsAffinity
A collection of iWorkUnits that must be run on the main thread.
Whole GetWorkUnitMainCount() const
Returns the amount of iWorkUnit ready to be scheduled in the Main pool.
std::vector< Resource * > Resources
This maintains ownership of all the thread specific resources.
virtual void AddWorkUnitMain(iWorkUnit *MoreWork, const String &WorkUnitName)
Add a normal Mezzanine::Threading::iWorkUnit to this For fcheduling.
void JoinAllThreads()
This is the 4th step (of 6) in a frame.
DefaultRollingAverage< Whole >::Type & GetFrameTimeRollingAverage()
Get The complete record of the durations of the last few frames.
virtual void CreateThreads()
This is the 2nd step (of 6) in a frame.
void CleanUpThreads()
Used in destruction to tear down threads.
virtual void SortWorkUnitsAll(bool UpdateDependentGraph_=true)
Sort all the WorkUnits that must run on the main thread to allow them to be used more efficiently in ...
virtual bool AreAllWorkUnitsComplete()
Is the work of the frame done?
MaxInt GetTimeStamp()
Get a timestamp, in microseconds. This will generally be some multiple of the GetTimeStampResolution ...
static void RemoveErrorScheduler(FrameScheduler *SchedulerToRemove)
Remove the passed scheduler from the list that will log in the event of an unhand;ed exception...
FrameScheduler(std::fstream *_LogDestination=0, Whole StartingThreadCount=GetCPUCount())
Create a Framescheduler that owns a filestream for logging.
Whole TargetFrameLength
The Maximum frame rate this algorithm should run at.
bool LoggingToAnOwnedFileStream
Set based on which constructor is called, and only used during destruction.
Integer TimingCostAllowance
To prevent frame time drift this many microseconds is subtracted from the wait period to allow time f...
virtual WorkUnitKey GetSortingKey(FrameScheduler &SchedulerToCount)=0
Get the sorting metadata.
std::ostream & GetLog()
Get the endpoint for the logs.
virtual iWorkUnit * GetNextWorkUnitAffinity()
Just like GetNextWorkUnit except that it also searches through and prioritizes work units with affini...
virtual void RemoveWorkUnitMain(iWorkUnit *LessWork)
Remove a WorkUnit from the main pool of WorkUnits (and not from the groups of Affinity or MonpolyWork...
Whole GetLastPauseTime() const
How long was the pause, if any, last frame?
std::vector< WorkUnitKey > WorkUnitsAffinity
A freshly sorted WorkUnitsAffinity or an empty vector.
DependentGraphType DependentGraph
This structure allows reverse lookup of dependencies.
virtual Whole GetThreadCount()
Get the amount of threads that will be used to execute WorkUnits a the start of the next frame...
Whole GetLastFrameTime() const
How long was the previous frame?
Interface of a WorkUnit. This represents on piece of work through time.
std::ostream * LogDestination
When the logs are aggregated, this is where they are sent.
std::vector< WorkUnitKey >::iterator IteratorMain
An iterator suitable for iterating over the main pool of work units.
This file defines the template double buffered resources that can be attached to a thread...
void DeleteThreads()
Simply iterates over and deletes everything in Threads.
virtual void RunMainThreadWork()
This is the 3rd step (of 6) in a frame.
void SwapBufferedResources()
This takes all active buffered resources offline (therfore available for async processing) and makes ...
A kind of workunit given exclusive runtime so it can consume time on multiple threads.
bool NeedToLogDeps
Brief Do we have to log ependencies have they changed since last logged?
Whole FrameCount
Used to store a count of frames from the begining of game execution.
Only used when a thread successfully attempts to gain ownership of a task, or some other tasks succes...
Whole GetWorkUnitAffinityCount() const
Returns the amount of iWorkUnit ready to be scheduled in the Affinity pool.
A thread specific collection of double-buffered and algorithm specific resources. ...
A small wrapper around the system thread.
std::vector< WorkUnitKey > WorkUnitsMain
A collection of all the work units that are not Monopolies and do not have affinity for a given threa...
Declares a tool for automatically unlocking a mutex in an exception safe way.
std::vector< MonopolyWorkUnit * >::iterator IteratorMonoply
An iterator suitable for iterating over the main pool of work units.
Int32 AtomicCompareAndSwap32(Int32 *VariableToChange, const Int32 &OldValue, const Int32 &NewValue)
Atomically Compares And Swaps a 32 bit value.
This file has the Declarations for the main FrameScheduler class.
void SetErrorHandler()
When Things crash the logs still needs to be flushed and other resources cleaned. This sets a functio...
virtual MaxInt GetCurrentFrameStart() const
When did this frame start?
MaxInt Now()
Get a timestamp, in microseconds. This will generally be some multiple of the GetTimeStampResolution ...
MaxInt CurrentPauseStart
What time did the current Frame Start at.
void DependenciesChanged(bool Changed=true)
Indicate to the framescheduler if dependencies need to be logged.
virtual void SetFrameLength(const Whole &FrameLength)
Set the Desired length of a frame in microseconds.
void Lock()
Lock the SpinLock.
SpinLock LogResources
Protects DoubleBufferedResources during creation and error handling from being accessed by the LogAgg...
virtual void RemoveWorkUnitAffinity(iWorkUnit *LessWork)
Remove a WorkUnit from the Affinity pool of WorkUnits (and not from the Main group or MonpolyWorkUnit...
virtual Whole GetFrameCount() const
Get the current number of frames that have elapsed.
virtual LogAggregator * GetLogAggregator()
Get the Log aggregation work unit if one exists for emergency loggin purposes only.
std::vector< WorkUnitKey > WorkUnitsMain
A freshly sorted WorkUnitsMain or an empty vector.
virtual void UpdateDependentGraph()
Create a reverse depedent graph that can be used for sorting Mezzanine::Threading::iWorkUnit "iWorkUn...
virtual void SetThreadCount(const Whole &NewThreadCount)
Set the amount of threads to use.
void MEZZ_LIB sleep_for(UInt32 MicroSeconds)
Blocks the calling thread for a period of time.
This is central object in this algorithm, it is responsible for spawning threads and managing the ord...
virtual ~FrameScheduler()
Destructor.
virtual void AddWorkUnitAffinity(iWorkUnit *MoreWork, const String &WorkUnitName)
Add a normal Mezzanine::Threading::iWorkUnit to this For scheduling.
void LogDependencies()
This sends the dependencies to the LogDestination (Skipping any thread specific resources) ...
Logger & GetUsableLogger()
Get the usable logger for this thread specific resource.
std::vector< WorkUnitKey >::iterator IteratorAffinity
An iterator suitable for iterating over the main pool of work units.
virtual void ResetAllWorkUnits()
This is the 5th step (of 6) in a frame.
void NextFlushForced(Boole Force=true)
Used to indicate the next log flush is abnormally forced.
DefaultRollingAverage< Whole >::Type & GetPauseTimeRollingAverage()
Get The complete record of the past durations of the Pauses Each frame.
This defines a number of workunits that are required for doing some tasks that the Framescheduler req...
virtual void RemoveWorkUnitMonopoly(MonopolyWorkUnit *LessWork)
Remove a WorkUnit from the Monopoly pool of WorkUnits (and not from the Main or Affinity group)...
Contains an interface for a kind of WorkUnit that consumes time on multiple thread.
Gather all the thread specific logs and commit them to the main log.
virtual iWorkUnit * GetNextWorkUnit()
Gets the next available workunit for execution.
virtual Whole GetFrameLength() const
Get the desired length of a frame.
void UpdateWorkUnitKeys(std::vector< WorkUnitKey > &Units)
Iterate over the passed container of WorkUnitKeys and refresh them with the correct data from their r...
std::vector< Thread * > Threads
A way to track an arbitrary number of threads.
MaxInt CurrentFrameStart
What time did the current Frame Start at.
Task is not yet started this frame, this can change without notice.
static void AddErrorScheduler(FrameScheduler *SchedulerToAdd)
Add this scheduler to the list that will log in the event of an unhand;ed exception.
Simple thread safe ways to check and change a specified variable atomically.
long long MaxInt
A large integer type suitable for compile time math and long term microsecond time keeping...
Thread has completed all work this from frame, will not change until this frame ends.
std::vector< MonopolyWorkUnit * > WorkUnitsMonopolies
A collection of all the monopolies this scheduler must run and keep ownership of. ...
virtual void RunAllMonopolies()
This is the 1st step (of 6) in a frame.
The bulk of the engine components go in this namspace.
unsigned long Whole
Whole is an unsigned integer, it will be at least 32bits in size.
WorkSorter * Sorter
If this pointer is non-zero then the WorkSorter it points at will be used to sort WorkUnits...
ThreadId MainThreadID
For some task it is important to know the ID of the main thread.
Logger * GetThreadUsableLogger(ThreadId ID=this_thread::get_id())
Get the logger safe to use this thread.
The thread ID is a unique identifier for each thread.
void Unlock()
Unlock the spinlock.
DefaultRollingAverage< Whole >::Type FrameTimeLog
A rolling average of Frame times.
virtual void DoWork(DefaultThreadSpecificStorage::Type &CurrentThreadStorage)
This does the actual work of log aggregation.
Whole GetWorkUnitMonopolyCount() const
Returns the amount of MonopolyWorkUnit ready to be scheduled.
std::string String
A datatype used to a series of characters.
void WaitUntilNextFrame()
This is the final step (of 6) in a frame.
Whole CurrentThreadCount
How many threads will this try to execute with in the next frame.
virtual void SortWorkUnitsAffinity(bool UpdateDependentGraph_=true)
Sort the WorkUnits that must run on the main thread to allow them to be used more efficiently in the ...
virtual Boole ForceLogFlush()
Forces the FrameScheduler to find Its LogAggregator and make it flush the logs if it can...
DefaultRollingAverage< Whole >::Type PauseTimeLog
A rolling average of Frame Pause times.