Suckless OGL 1.0.0
A lean, high-performance C11 PBR Renderer
Loading...
Searching...
No Matches
async_loader.c File Reference
#include "async_loader.h"
#include "asset_manager.h"
#include "async_backend.h"
#include "log.h"
#include "perf_timer.h"
#include "profiler.h"
#include "tracy_manager.h"
#include "utils.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "app.h"
#include "app_profiling.h"
Include dependency graph for async_loader.c:

Data Structures

struct  AsyncLoader
 

Macros

#define transition_tracy_state(s)    tracy_manager_async_transition(loader->tracy_mgr, s)
 
#define cleanup_tracy_states()   tracy_manager_async_end(loader->tracy_mgr)
 

Functions

static void async_perform_conversion (AsyncLoader *loader)
 Performs the actual format conversion (e.g., float -> half float) into the PBO.
 
static void async_cleanup_payload (AsyncLoader *loader)
 Destroys backend-specific payload allocations.
 
static void async_wait_and_convert (AsyncLoader *loader)
 Worker thread wait loop for PBO allocation from the main thread.
 
static void async_handle_io (AsyncLoader *loader, const char *path_to_load)
 Heavy-lifting disk IO loading phase.
 
static void * async_worker_func (void *arg)
 Thread entry point for the background worker.
 
AsyncLoader * async_loader_create (struct TracyManager *mgr)
 Initialize the asynchronous loader subsystem.
 
void async_loader_destroy (AsyncLoader *loader)
 Tear down the asynchronous loader subsystem.
 
bool async_loader_request (AsyncLoader *loader, const AssetHandle *asset)
 Request a new asset to be loaded asynchronously.
 
bool async_loader_poll (AsyncLoader *loader, AsyncRequest *out_req)
 Poll the status of the current request.
 
void async_loader_provide_pbo (AsyncLoader *loader, void *mapped_ptr, GLuint pbo_id)
 Provide the mapped memory address and GL buffer ID for PBO writing.
 
void async_loader_cancel (AsyncLoader *loader)
 Cancel a loading request waiting for PBO input.
 
int async_loader_subsys_init (App *app)
 
void async_loader_subsys_cleanup (App *app)
 

Macro Definition Documentation

◆ cleanup_tracy_states

#define cleanup_tracy_states ( )    tracy_manager_async_end(loader->tracy_mgr)

◆ transition_tracy_state

#define transition_tracy_state (   s)     tracy_manager_async_transition(loader->tracy_mgr, s)

Function Documentation

◆ async_cleanup_payload()

static void async_cleanup_payload ( AsyncLoader *  loader)
static

Destroys backend-specific payload allocations.

Invokes the active format backend's cleanup function to free CPU allocations loaded during the IO phase.

Parameters
loaderPointer to the AsyncLoader instance.

Concurrency constraints:

  • PRECONDITION: request_mutex MUST be HELD on entry.
  • POSTCONDITION: request_mutex is HELD on exit.
Here is the call graph for this function:

◆ async_handle_io()

static void async_handle_io ( AsyncLoader *  loader,
const char *  path_to_load 
)
static

Heavy-lifting disk IO loading phase.

Loads the asset payload from disk into temporary CPU memory. This is executed entirely outside the request_mutex to keep disk latency from blocking the main thread.

Parameters
loaderPointer to the AsyncLoader instance.
path_to_loadAbsolute or relative path to the asset on disk.

Concurrency constraints:

  • PRECONDITION: request_mutex MUST NOT be held on entry (called without lock).
  • POSTCONDITION: request_mutex is HELD on exit.
  • State on entry: ASYNC_LOADING.
  • State on exit: ASYNC_WAITING_FOR_PBO (success) or ASYNC_FAILED (failure).
Here is the call graph for this function:

◆ async_loader_cancel()

void async_loader_cancel ( AsyncLoader *  loader)

Cancel a loading request waiting for PBO input.

Aborts the current request if it is still waiting for the main thread to provide a PBO.

Parameters
loaderLoader instance.

State transitions:

  • Expected Input State: ASYNC_WAITING_FOR_PBO.
  • On success: Transits to ASYNC_FAILED, signals the worker thread to cleanup.
  • If called in any other state (e.g. ASYNC_CONVERTING): Ignored to prevent data corruption.

Concurrency: Thread-safe. Acquires request_mutex.

◆ async_loader_create()

AsyncLoader * async_loader_create ( struct TracyManager *  mgr)

Initialize the asynchronous loader subsystem.

Allocates resources, initializes synchronization primitives (mutex, condvar), and spawns the background worker thread.

Parameters
mgrTracy profiler manager instance.
Returns
A new AsyncLoader instance on success, NULL on failure.

Concurrency: Thread-safe. Lock initialized inside. Initial state: ASYNC_IDLE.

Here is the call graph for this function:

◆ async_loader_destroy()

void async_loader_destroy ( AsyncLoader *  loader)

Tear down the asynchronous loader subsystem.

Signals the worker thread to exit, waits for its completion (joins it), releases any pending resources/requests, and destroys synchronization primitives.

Parameters
loaderLoader instance to destroy. Can be NULL (no-op).

Concurrency: Safe to call from the main thread during cleanup. Post-condition: Mutex/condvar are destroyed and memory is freed.

Here is the call graph for this function:

◆ async_loader_poll()

bool async_loader_poll ( AsyncLoader *  loader,
AsyncRequest *  out_req 
)

Poll the status of the current request.

Checks if the request is ready for GPU upload, waiting for PBO allocation, or has failed. This is meant to be called periodically from the main thread.

Parameters
loaderLoader instance.
out_reqDestination structure to copy the request snapshot if ready/waiting.
Returns
true if a state transition needs handling (READY, WAITING_FOR_PBO, FAILED), false otherwise.

State transitions:

  • Expected Input State: Any state.
  • If current state is ASYNC_READY: Copies request, resets state to ASYNC_IDLE, returns true.
  • If current state is ASYNC_FAILED: Copies request, resets state to ASYNC_IDLE, returns true.
  • If current state is ASYNC_WAITING_FOR_PBO: Copies request, state remains ASYNC_WAITING_FOR_PBO, returns true.
  • For any other state: Returns false (no action needed yet).

Concurrency: Thread-safe. Acquires request_mutex.

◆ async_loader_provide_pbo()

void async_loader_provide_pbo ( AsyncLoader *  loader,
void *  mapped_ptr,
GLuint  pbo_id 
)

Provide the mapped memory address and GL buffer ID for PBO writing.

Called by the main thread after detecting ASYNC_WAITING_FOR_PBO during poll. Maps the CPU pointer to the PBO and allows the worker to perform the conversion.

Parameters
loaderLoader instance.
mapped_ptrPointer to the mapped buffer memory (CPU visible).
pbo_idOpenGL buffer object ID (or 0 for CPU-only malloc fallback).

State transitions:

  • Expected Input State: ASYNC_WAITING_FOR_PBO.
  • On success: Transits to ASYNC_CONVERTING, signals the worker thread to resume.
  • If called in any other state: Ignored (no-op).

Concurrency: Thread-safe. Acquires request_mutex.

◆ async_loader_request()

bool async_loader_request ( AsyncLoader *  loader,
const AssetHandle *  asset 
)

Request a new asset to be loaded asynchronously.

Accepts a loading task if the loader is idle or finished with the previous one.

Parameters
loaderLoader instance.
assetPath and format metadata of the asset.
Returns
true if the request was accepted, false if the loader was busy or invalid.

State transitions:

  • Expected Input State: ASYNC_IDLE, ASYNC_FAILED, or ASYNC_READY.
  • On success: Transits to ASYNC_PENDING, signals the worker thread.
  • On failure: State remains unchanged (loader is currently busy: ASYNC_LOADING, ASYNC_WAITING_FOR_PBO, ASYNC_CONVERTING).

Concurrency: Thread-safe. Acquires request_mutex.

Here is the call graph for this function:

◆ async_loader_subsys_cleanup()

void async_loader_subsys_cleanup ( App *  app)
Here is the call graph for this function:

◆ async_loader_subsys_init()

int async_loader_subsys_init ( App *  app)
Here is the call graph for this function:

◆ async_perform_conversion()

static void async_perform_conversion ( AsyncLoader *  loader)
static

Performs the actual format conversion (e.g., float -> half float) into the PBO.

Runs the format-specific backend conversion. Since writing to mapped PBO memory can be slow, the request mutex is released during the conversion process to keep the main thread from stalling.

Parameters
loaderPointer to the AsyncLoader instance.

Concurrency constraints:

  • PRECONDITION: request_mutex MUST be HELD on entry.
  • POSTCONDITION: request_mutex is HELD on exit.
  • Mutex is temporarily released during backend->convert().
  • Expected state on entry: ASYNC_CONVERTING.
  • State on exit: ASYNC_READY.
Here is the call graph for this function:

◆ async_wait_and_convert()

static void async_wait_and_convert ( AsyncLoader *  loader)
static

Worker thread wait loop for PBO allocation from the main thread.

Blocks the worker thread until the main thread allocates a PBO, maps it, and calls async_loader_provide_pbo() (which transitions state to ASYNC_CONVERTING and signals request_cond).

Parameters
loaderPointer to the AsyncLoader instance.

Concurrency constraints:

  • PRECONDITION: request_mutex MUST be HELD on entry.
  • POSTCONDITION: request_mutex is HELD on exit.
  • Temporarily releases the mutex during pthread_cond_wait().
  • Expected state on entry: ASYNC_WAITING_FOR_PBO.
  • State on exit: ASYNC_READY (success) or ASYNC_FAILED (cancel/shutdown).
Here is the call graph for this function:

◆ async_worker_func()

static void * async_worker_func ( void *  arg)
static

Thread entry point for the background worker.

Handles the main execution loop, waiting passively for submitted requests, coordinating IO and conversion task dispatches.

Parameters
argOpaque pointer to the AsyncLoader instance.
Returns
NULL.
Here is the call graph for this function: