TeamPorcupine / ProjectPorcupine

Project Porcupine: A Base-Building Game...in Space!
GNU General Public License v3.0
484 stars 279 forks source link

Planning for Async/Non-Blocking World Loading #1784

Open bjubes opened 7 years ago

bjubes commented 7 years ago

As discussed in #1593, one of the largest lag spikes in the game is loading a new world or save. Since this is not done asyncronously, the build appears to have crashed and prompts the user to force close the program. Fixing this is a complex task, So i'm putting together a place to create a solution.

The Problem

When the new world is loaded, a variety of slow operation all fire at once.

The Solution

I plan to focus on the fourth bullet, the constructors/start methods of every single manager and controller called when the game loads. These can be split into two categories: Unity centric and non Unity centric. The former interact with some aspect of unity that cannot be put in another thread, like Resource.Load or instanciating gameobjects. the latter only do calculations or can be tailored to do most of their work asyncronously and do the unity specific-work in a callback.

First every start method and constructor has to be analyzed to see what category it falls into. Next, a "master" object will be repsponsible for invoking all start/initialization related calls. Those that have no unity interaction can be run in another thread, while the unity centric ones are put into a queue inside the master object. The master object starts a couroutine where it cycles through the queue like so

//pseudo-code - inside a coroutine
start a timer
while ( timer < maxTimeAllowedPerFrame) {
nextItemInQueue();
}
yield return null;

so that the longest hang time is reduced to the time it takes one function to run/the maxTimeAllowed. Obviously this could take a very long time to complete, so it is important to thread as many calculations and split up large calls into smaller parts. While the queue is still full, the user simply gets a "loading..." dialog and the game will never lag long enough for the OS to report it as not working.

last note: any processes that need to happen in a specific order can hopefully be achieved by a series of callbacks, so that when the first one finishes it puts the next callback into the master queue.

This video is an implementation of a similar system. Its used for pathfinding but the general idea is the same.