Latest version:
Tramway SDK 0.0.9
Github
Quick links
Home
Get Started

Async


The Async system lets you do stuff... asynchronously. On a separate worker thread. This meant that the main logic thread can be freed up from certain computations and in general makes the framework go vroom vroom.

Currently the async system supports Resource loading. In the future it will get some more functionality, but it's only resources for now.

To use it, you need to initialize the system. This will start up the worker threads. The system needs to be updated in the main loop and then shut down.

Unfortunately, it seems like multithreading still doesn't work in webassembly builds, so you will have to do resource loading entirely from the main thread.

Resource streaming


Let's take the Model resource as an example. It is derived from the Resource class and implements its API.

Most of the time while loading the Model is spent retrieving it from the disk and parsing it. All of the relevant logic is implemented in the LoadFromDisk() method. This method will be called from the worker thread.

Due to a quirk in the OpenGL API, we need to perform all of the data uploads to the GPU from the main thread. The relevant logic for this is implemented in the LoadFromMemory() method.

Example



This is what happens when you call the SetModel() method on a RenderComponent and if the Model isn't loaded yet.

What happens when you assign a Model to a RenderComponent?

  1. The RenderComponent will check if the Model is loaded. If it is not, it will pass the Model to the Async system, which will add it to the disk loading queue.
  2. The worker thread will pop the Model from the disk loading queue and will call its LoadFromDisk() method. The method will load the .stmdl or .dymdl file associated with the Model, parse it into binary format and build up an AABB tree. The worker will then add the resource to the memory loading queue.
  3. The main thread will pop the Model from the memory loading queue and will call its LoadFromMemory() method. The method will pass the binary model data to the Render API, which in turn will upload the model data to the GPU memory. The memory containing the binary data will then be free. Finally the Model will be added to the finishing queue.
  4. The main thread will pop the Model from the finishing queue. It will then check which EntityComponent requested the resource to be loaded. It will then notify our RenderComponent about the loaded resource.
  5. The RenderComponent will then add the loaded 3D model to the scene. It will then be eligible to be rendered to the screen.

Programming in C++


#include <framework/async.h>
API documentation page.


const size_t thread_count = 1;

Async::Init(thread_count);

// main loop
while (true) {

    // the worker thread won't start on webassembly
    // builds, so we just do that part on the
    // main thread

#ifdef __EMSCRIPTEN__
    Async::LoadResourcesFromDisk();
#endif

    Async::LoadResourcesFromMemory();
    Async::FinishResources();
}

Async::Yeet();

It is possible to request a resource to be loaded by calling the Async::RequestResource() function, but a better way would be by using the ResourceProxy smart pointer, which will not only automatically request a resource to be loaded, but will also automatically manage reference count.

If you want to create a progress bar which displays the process of resource loading, you can use Async::GetWaitingResources() to get the number of resources being processed by the Async system.