Its important to handle the case of breakpoints, otherwise the program will likely crash and burn when resuming, assuming the timer/frame system hasn't stopped
// If dt is too large, we must have resumed from a
// breakpoint; in this case frame-lock the target rate
// for this frame
if (delta_time > 1.0f)
{
delta_time = 1.0f/30.0F;
}
To make use of multithreading, suggested to use a thread pool job system
namespace job
{
// signature of all job entry points
typedef void EntryPoint(uintptr_t param);
// allowable priorities
enum class Priority
{
LOW, NORMAL, HIGH, CRITICAL
};
// counter (implementation not shown)
struct Counter ... ;
Counter* AllocCounter();
void FreeCounter(Counter* pCounter);
// simple job declaration
struct Declaration
{
EntryPoint *m_pEntryPoint;
uintptr_t m_param;
Priority m_priority;
Counter* m_pCounter;
};
// kick a job
void KickJob(const Declaration& decl);
void KickJobs(int count, const Declaration aDecl[]);
// wait for job to terminate (for its Counter to become zero)
void WaitForCounter(Counter* pCounter);
// kick jobs and wait for completion
void KickJobAndWait(const Declaration& decl);
void KickJobsAndWait(int count, const Declaration aDecl[]);
void* JobWorkerThread(void*)
{
// keep on running jobs forever...
while (true)
{
Declaration declCopy;
// wait for a job to become available
pthread_mutex_lock(&g_mutex);
while (!g_ready)
{
pthread_cond_wait(&g_jobCv, &g_mutex);
}
// copy the JobDeclaration locally and
// release our mutex lock
declCopy = GetNextJobFromQueue();
pthread_mutex_unlock(&g_mutex);
// run the job
declCopy.m_pEntryPoint(declCopy.m_param);
// job is done! rinse and repeat...
}
}
}
// example usage
void NpcThinkJob(uintparam_t param)
{
Npc* pNpc = reinterpret_cast<Npc*>(param);
pNpc->StartThinking();
pNpc->DoSomeMoreUpdating();
// ...
// BAD! calling another job/thread won't work:
// now let's cast a ray to see if we're aiming
// at anything interesting -- this involves
// kicking off another job that will run on
// a different code (worker thread)
RayCastHandle hRayCast = CastGunAimRay(pNpc);
// the results of the ray cast aren't going to
// be ready until later this frame, so let's
// go to sleep until it's ready
WaitForRayCast(hRayCast);
// zzz...
// wake up!
// now fire my weapon, but only if the ray
// cast indicates that we are aiming at an
// enemy
pNpc->TryFireWeaponAtTarget(hRayCast);
}
For an excellent in-depth discussion of how the Naughty Dog job sys- tem was built, and why it was built that way, check out Christian Gyrling’s excellent GDC 2015 talk entitled, “Parallelizing the Naughty Dog Engine,” which is available at https://bit.ly/2H6v0J4. Christian’s slides are available at https://bit.ly/2ETr5x9.