Skip to content

Planning for Async/Non-Blocking World Loading #1784

@bjubes

Description

@bjubes

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.

  • loading the save file/ generating a new world
  • Destroying everything in the current scene, and the resulting garbage collection
  • Instantiating every game object in the new scene, and the resulting memory allocation and unity lag that comes with Instanciating.
  • running the start method of every object in the _World, which then calls almost every constructor of every manager, controller, etc...
  • If its a save, the characters may all have a pathfinding call on top of that.

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions