## Frame Based vs Time Based Events * Use time based!!! Bad to have game speed be dependant on frame rate as the game will run differently depending on your machine * Modern CPUs have high resolution clocks to base time off of * Can also calculate time based on number of CPU ticks and CPU frequency (e.g. 100 Ticks @ 3 Hz = 100 Ticks @ 3 Ticks/sec = ~33 Seconds) ## Event System * If you store game changes via events (e.g. movement, collision, etc.), you can store and replay the gameplay by storing all of the events and game starting state * You can then play back at any speed you want, as long as you have control of the game speed (i.e. not frame rate based), which is useful for debugging ## Managing Breakpoints * 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; } ## Game Job System * 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(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); } * Note that in this version, a job cannot sleep/block/call another job, as that would cause call stack issues * To handle this, use coroutines/fibers/job counters * Job counters are like semaphores in that they have a Count, and when the count goes to zero that means all sub-jobs (called by the current job) and the current job have finished * Recommend using spin locks instead of regular sleeping/blocking locks, otherwise the entire current job-thread will go to sleep * Recommend implementing visualization and profiling tools for the system otherwise its very hard to debug * Naughty dog job system has visual tool which shows each cpu core and color-coded jobs ran: ![Naughty Dog Job Visualization](thread_vis.png) >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.