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?
-
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.
-
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.
-
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.
-
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.
-
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.
|