+++ /dev/null
-#include "vg_async.h"
-
-struct vg_async vg_async;
-
-u32 vg_thread_purpose(void);
-
-vg_async_item *vg_async_alloc( u32 size )
-{
- /* ditch out here if engine crashed. this serves as the 'quit checking' */
- if( SDL_AtomicGet( &vg.engine_status ) == k_engine_status_crashed )
- longjmp( vg.env_loader_exit, 1 );
-
- SDL_LockMutex( vg_async.lock );
-
- u32 total_allocation = vg_align8(size) + vg_align8(sizeof(vg_async_item)),
- remaining = vg_linear_remaining( vg_async.buffer ),
- capacity = vg_linear_get_capacity( vg_async.buffer );
-
- if( total_allocation > capacity )
- {
- SDL_UnlockMutex( vg_async.lock );
- vg_fatal_condition();
- vg_info( "async alloc invalid size\n" );
- vg_info( "Requested: %umb. Buffer size: %umb\n",
- (total_allocation/1024)/1024,
- (capacity/1024)/1024 );
- vg_fatal_exit();
- }
-
- if( total_allocation > remaining )
- {
- SDL_UnlockMutex( vg_async.lock );
- SDL_SemWait( vg_async.sem_wait_for_flush );
- SDL_LockMutex( vg_async.lock );
-
- remaining = vg_linear_remaining( vg_async.buffer );
- capacity = vg_linear_get_capacity( vg_async.buffer );
- }
-
- void *block = vg_linear_alloc( vg_async.buffer, total_allocation );
-
- vg_async_item *entry = block;
- entry->next = NULL;
-
- if( size ) entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item));
- else entry->payload = NULL;
-
- entry->size = size;
- entry->fn_runner = NULL;
-
- if( vg_async.end ){
- vg_async.end->next = entry;
- vg_async.end = entry;
- }else{
- vg_async.start = entry;
- vg_async.end = entry;
- }
-
- SDL_UnlockMutex( vg_async.lock );
-
- return entry;
-}
-
-/*
- * Wait until the current stack of async calls is completely flushed out
- */
-void vg_async_stall(void)
-{
- VG_ASSERT( vg_thread_purpose() == k_thread_purpose_loader );
- SDL_SemWait( vg_async.sem_wait_for_flush );
-}
-
-void vg_async_dispatch( vg_async_item *item,
- void (*runner)( void *payload, u32 size ) )
-{
- VG_ASSERT( vg_thread_purpose() == k_thread_purpose_loader );
- if( SDL_SemValue(vg_async.sem_wait_for_flush) )
- SDL_SemWait(vg_async.sem_wait_for_flush);
-
- SDL_LockMutex( vg_async.lock );
- item->fn_runner = runner;
- SDL_UnlockMutex( vg_async.lock );
-}
-
-void vg_async_call( void (*runner)( void *payload, u32 size ),
- void *payload, u32 size )
-{
- VG_ASSERT( vg_thread_purpose() == k_thread_purpose_loader );
- vg_async_item *call = vg_async_alloc(0);
- call->payload = payload;
- call->size = size;
- vg_async_dispatch( call, runner );
-}
-
-void vg_run_async_checked(void)
-{
- SDL_LockMutex( vg_async.lock );
-
- while( vg_async.start )
- {
- vg_async_item *entry = vg_async.start;
-
- if( entry->fn_runner )
- {
- entry->fn_runner( entry->payload, entry->size );
- vg_async.start = entry->next;
-
- if( vg_async.start == NULL )
- {
- vg_async.end = NULL;
-
- vg_linear_clear( vg_async.buffer );
-
- if( !SDL_SemValue( vg_async.sem_wait_for_flush ) )
- {
- SDL_SemPost( vg_async.sem_wait_for_flush );
- }
- }
- }
- else
- {
- SDL_UnlockMutex( vg_async.lock );
- return;
- }
- }
-
- if( !SDL_SemValue( vg_async.sem_wait_for_flush ) )
- {
- SDL_SemPost( vg_async.sem_wait_for_flush );
- }
-
- SDL_UnlockMutex( vg_async.lock );
-}
-
-void vg_async_init(void)
-{
- vg_async.lock = SDL_CreateMutex();
- VG_ASSERT( vg_async.lock );
-
- vg_async.sem_wait_for_flush = SDL_CreateSemaphore(0);
- VG_ASSERT( vg_async.sem_wait_for_flush );
-
- vg_async.buffer = vg_create_linear_allocator( NULL, 50*1024*1024, VG_MEMORY_SYSTEM );
-}
+++ /dev/null
-/* Copyright (C) 2021-2025 Harry Godden (hgn) - All Rights Reserved
- *
- * primateves that you use when you need to run something from another thread
- * back in the main loop of vg, at the start of each frame
- *
- * TODO: Replace this with async2 in the future.
- */
-
-#pragma once
-#include "vg_engine.h"
-
-static void vg_assert_thread( u32 required );
-
-typedef struct vg_async_item vg_async_item;
-struct vg_async_item
-{
- vg_async_item *next;
-
- void *payload;
- u32 size;
-
- void (*fn_runner)( void *payload, u32 size );
-};
-
-struct vg_async
-{
- void *buffer;
-
- vg_async_item *start, *end;
-
- SDL_sem *sem_wait_for_flush;
- SDL_mutex *lock;
-}
-extern vg_async;
-
-void vg_async_init(void);
-
-/*
- * Make a simple async call without allocating extra.
- */
-void vg_async_call( void (*runner)( void *payload, u32 size ), void *payload, u32 size );
-
-/*
- * Run as much of the async buffer as possible
- */
-void vg_run_async_checked(void);
-
-/*
- * Allocate an asynchronous call with a bit of memory
- */
-vg_async_item *vg_async_alloc( u32 size );
-
-/*
- * Mark the call as being filled and ready to go
- */
-void vg_async_dispatch( vg_async_item *item, void (*runner)( void *payload, u32 size ) );
-
-/*
- * Wait until the current stack of async calls is completely flushed out
- */
-void vg_async_stall(void);
bool vg_init_async_queue( vg_async_queue *queue )
{
- if( pthread_mutex_init( &queue->lock, NULL ) )
+ if( !VG_MUTEX_INIT( queue->lock ) )
goto e0;
- if( pthread_mutex_init( &queue->data_lock, NULL ) )
+ if( !VG_MUTEX_INIT( queue->data_lock ) )
goto e1;
- if( sem_init( &queue->blocking_memory_signal, 0, 0 ) )
+ if( !VG_SEMAPHORE_INIT( queue->blocking_signal, 0 ) )
goto e2;
- if( sem_init( &queue->work_semaphore, 0, 0 ) )
+ if( !VG_SEMAPHORE_INIT( queue->work_semaphore, 0 ) )
goto e3;
queue->queue.buffer = malloc( queue->buffer_size );
goto e2;
return 1;
-e3: sem_destroy( &queue->blocking_memory_signal );
-e2: pthread_mutex_destroy( &queue->data_lock );
-e1: pthread_mutex_destroy( &queue->lock );
+e3: VG_SEMAPHORE_FREE( queue->blocking_signal );
+e2: VG_MUTEX_FREE( queue->data_lock );
+e1: VG_MUTEX_FREE( queue->lock );
e0: return 0;
}
void vg_free_async_queue( vg_async_queue *queue )
{
- pthread_mutex_destroy( &queue->data_lock );
- pthread_mutex_destroy( &queue->lock );
- sem_destroy( &queue->blocking_memory_signal );
+ VG_MUTEX_FREE( queue->data_lock );
+ VG_MUTEX_FREE( queue->lock );
+ VG_SEMAPHORE_FREE( queue->work_semaphore );
+ VG_SEMAPHORE_FREE( queue->blocking_signal );
}
-vg_async_task *vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking )
+vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking, const char *debug_info )
{
VG_ASSERT( bytes <= queue->queue.size );
u32 total_size = sizeof(vg_async_task) + bytes;
- pthread_mutex_lock( &queue->data_lock );
- pthread_mutex_lock( &queue->lock );
+ VG_MUTEX_LOCK( queue->data_lock );
+ if( queue->allocating_task )
+ {
+ vg_fatal_error( "Overlapping async allocations. \n"
+ " Previous allocation began at: %s\n"
+ " Overlapping call at: %s\n", queue->allocating_task->alloc_debug_info, debug_info );
+ }
+ VG_MUTEX_LOCK( queue->lock );
vg_queue *ring = &queue->queue;
vg_async_task *task = vg_queue_alloc( ring, total_size, NULL );
while( blocking && !task )
{
- pthread_mutex_unlock( &queue->lock );
- sem_wait( &queue->blocking_memory_signal );
- pthread_mutex_lock( &queue->lock );
+ VG_MUTEX_UNLOCK( queue->lock );
+ VG_SEMAPHORE_WAIT( queue->blocking_signal );
+ VG_MUTEX_LOCK( queue->lock );
task = vg_queue_alloc( ring, total_size, NULL );
}
if( task )
+ {
task->handler = NULL;
+ task->queue = queue;
+ task->alloc_debug_info = debug_info;
+ queue->allocating_task = task;
+ }
- pthread_mutex_unlock( &queue->lock );
+ VG_MUTEX_UNLOCK( queue->lock );
return task;
}
-void vg_async_task_dispatch( vg_async_queue *queue, vg_async_task *task, void (*handler)( vg_async_task *task ) )
+void _vg_async_task_dispatch( vg_async_task *task, void (*handler)( vg_async_task *task ), const char *debug_info )
{
- pthread_mutex_unlock( &queue->data_lock );
-
+ task->queue->allocating_task = NULL;
+ VG_MUTEX_UNLOCK( task->queue->data_lock );
task->handler = handler;
- sem_post( &queue->work_semaphore );
+ task->alloc_debug_info = debug_info;
+ VG_SEMAPHORE_POST( task->queue->work_semaphore );
}
bool vg_async_has_work( vg_async_queue *queue )
{
- int work_count = 0;
- sem_getvalue( &queue->work_semaphore, &work_count );
- return work_count > 0? 1: 0;
+ return VG_SEMAPHORE_VALUE( queue->work_semaphore ) > 0? 1: 0;
}
bool vg_async_process_next_task( vg_async_queue *queue )
{
- sem_wait( &queue->work_semaphore );
-
- pthread_mutex_lock( &queue->lock );
+ VG_SEMAPHORE_WAIT( queue->work_semaphore );
+ VG_MUTEX_LOCK( queue->lock );
if( queue->quit == k_async_quit_immediate )
{
- pthread_mutex_unlock( &queue->lock );
+ VG_MUTEX_UNLOCK( queue->lock );
return 0;
}
else if( queue->quit == k_async_quit_when_empty )
{
if( queue->queue.allocation_count == 0 )
{
- pthread_mutex_unlock( &queue->lock );
+ VG_MUTEX_UNLOCK( queue->lock );
return 0;
}
}
vg_async_task *task = vg_queue_tail_data( &queue->queue );
- pthread_mutex_unlock( &queue->lock );
+ VG_MUTEX_UNLOCK( queue->lock );
if( task )
{
+ if( !task->handler )
+ vg_fatal_error( "NO HANDLER ALLOCATED FROM %s\n", task->alloc_debug_info );
+
task->handler( task );
- pthread_mutex_lock( &queue->lock );
+ VG_MUTEX_LOCK( queue->lock );
vg_queue_pop( &queue->queue );
- pthread_mutex_unlock( &queue->lock );
+ VG_MUTEX_UNLOCK( queue->lock );
}
- int status = 1;
- sem_getvalue( &queue->blocking_memory_signal, &status );
- if( status <= 0 )
- sem_post( &queue->blocking_memory_signal );
+ if( VG_SEMAPHORE_VALUE( queue->blocking_signal ) <= 0 )
+ VG_SEMAPHORE_POST( queue->blocking_signal );
return 1;
}
void vg_async_queue_end( vg_async_queue *queue, enum async_quit quit )
{
- pthread_mutex_lock( &queue->lock );
+ VG_MUTEX_LOCK( queue->lock );
queue->quit = quit;
- pthread_mutex_unlock( &queue->lock );
- sem_post( &queue->work_semaphore );
+ VG_MUTEX_UNLOCK( queue->lock );
+ VG_SEMAPHORE_POST( queue->work_semaphore );
+}
+
+struct simple_function_info
+{
+ void (*fn)(void *userdata);
+ void *userdata;
+};
+
+static void simple_function_call( vg_async_task *task )
+{
+ struct simple_function_info *info = (void *)task->data;
+ info->fn( info->userdata );
+}
+
+void vg_async_call( vg_async_queue *queue, void(*fn)(void *userdata), void *userdata )
+{
+ vg_async_task *task = vg_allocate_async_task( queue, sizeof(struct simple_function_info), 1 );
+ struct simple_function_info *info = (void *)task->data;
+ info->fn = fn;
+ info->userdata = userdata;
+ vg_async_task_dispatch( task, simple_function_call );
}
#pragma once
#include "vg/vg_mem.h"
#include "vg/vg_mem_queue.h"
-
-#include <pthread.h>
-#include <semaphore.h>
+#include "vg/vg_mutex.h"
typedef struct vg_async_queue vg_async_queue;
typedef struct vg_async_task vg_async_task;
u32 buffer_size;
u32 requested_bytes;
- sem_t blocking_memory_signal;
- sem_t work_semaphore;
- pthread_mutex_t lock, data_lock;
+ vg_semaphore blocking_signal;
+ vg_semaphore work_semaphore;
+ vg_mutex lock;
+ vg_mutex data_lock;
vg_queue queue;
+ vg_async_task *allocating_task;
enum async_quit
{
struct vg_async_task
{
+ vg_async_queue *queue;
+ const char *alloc_debug_info;
void (*handler)( vg_async_task *task );
u8 data[];
};
void vg_free_async_queue( vg_async_queue *queue );
/* returns NULL if out of memory, or if blocking is set, wait for memory to become availible */
-vg_async_task *vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking );
-void vg_async_task_dispatch( vg_async_queue *queue, vg_async_task *task, void (*handler)( vg_async_task *task ) );
-void vg_async_call_function( vg_async_queue *queue, void (*function)(void *, u32), void *userdata, u32 usersize );
+#define vg_allocate_async_task( Q,B,BL ) _vg_allocate_async_task( Q,B,BL, VG_LOG_WHERE )
+vg_async_task *_vg_allocate_async_task( vg_async_queue *queue, u32 bytes, bool blocking, const char *debug_info );
+
+#define vg_async_task_dispatch( T,H ) _vg_async_task_dispatch( T,H, VG_LOG_WHERE )
+void _vg_async_task_dispatch( vg_async_task *task, void (*handler)( vg_async_task *task ), const char *debug_info );
void vg_async_queue_end( vg_async_queue *queue, enum async_quit quit );
bool vg_async_has_work( vg_async_queue *queue );
bool vg_async_process_next_task( vg_async_queue *queue );
+void vg_async_call( vg_async_queue *queue, void(*fn)(void *userdata), void *userdata );
#include "vg_audio_dsp.h"
#include "vg_mem.h"
-#include "vg_async.h"
struct vg_dsp vg_dsp;
vg_strcat( &cmd, " -flto \\\n" );
/* want a lot of warnings but not useless ones */
- vg_strcat( &cmd, " -Wall -ferror-limit=8\\\n"
+ vg_strcat( &cmd, " -Wall -ferror-limit=10\\\n"
" -Wno-unused-function -Wno-unused-variable\\\n"
" -Wno-unused-command-line-argument -Wno-unused-but-set-variable\\\n"
);
#include "vg_engine.h"
-#include "vg_async.h"
+#include "vg_async2.h"
struct vg_engine vg =
{
.time_rate = 1.0,
.time_fixed_delta = VG_TIMESTEP_FIXED,
+
+ .main_tasks =
+ {
+ .buffer_size = 20*1024*1024
+ },
+ .loader_tasks =
+ {
+ .buffer_size = 20*1024*1024
+ }
};
const u32 k_thread_purpose_nothing = 0x00;
}
};
-static void async_vg_bake_shaders( void *payload, u32 size )
-{
- vg_shaders_compile();
-}
-
void vg_bake_shaders(void)
{
vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL );
- vg_async_call( async_vg_bake_shaders, NULL, 0 );
-}
-
-void async_internal_complete( void *payload, u32 size )
-{
- vg_success( "Internal async setup complete\n" );
-
- if( SDL_AtomicGet( &vg.engine_status ) == k_engine_status_crashed )
- return;
- else
- SDL_AtomicSet( &vg.engine_status, k_engine_status_running );
-
- vg_magi_restore();
+ vg_async_call( &vg.main_tasks, vg_shaders_compile, NULL );
}
#ifdef VG_CUSTOM_SHADERS
void vg_auto_shader_register(void); /* created from codegen */
#endif
-static void _vg_load_full( void *data )
+static void _vg_load_step0(void *_);
+static void _vg_load_step1(void *_);
+static void _vg_load_step2(void *_);
+static void _vg_load_step_complete(void *_);
+
+static void _vg_load_step0(void *_)
{
+ THREAD_1;
vg_info(" Copyright . . . -----, ,----- ,---. .---. \n" );
vg_info(" 2021-2025 |\\ /| | / | | | | /| \n" );
vg_info(" | \\ / | +-- / +----- +---' | / | \n" );
vg_preload();
vg_tex2d_replace_with_error_async( 0, &vg.tex_missing );
- vg_async_stall();
+ vg_async_call( &vg.main_tasks, _vg_load_step1, NULL );
+}
+
+static void _vg_load_step1(void *_)
+{
+ THREAD_0;
+ vg_async_call( &vg.loader_tasks, _vg_load_step2, NULL );
+}
+
+static void _vg_load_step2(void *_)
+{
+ THREAD_1;
vg_ui.tex_bg = vg.tex_missing;
/* internal */
vg_auto_shader_register();
#endif
vg_load();
+}
- vg_async_call( async_internal_complete, NULL, 0 );
-
+void _vg_load_temp_end(void)
+{
+ vg_async_call( &vg.main_tasks, _vg_load_step_complete, NULL );
vg_success( "Client loaded in %fs\n", vg.time_real );
}
+static void _vg_load_step_complete(void *_)
+{
+ vg_success( "Internal async setup complete\n" );
+
+ if( SDL_AtomicGet( &vg.engine_status ) == k_engine_status_crashed )
+ return;
+ else
+ SDL_AtomicSet( &vg.engine_status, k_engine_status_running );
+
+ vg_magi_restore();
+}
+
static void _vg_process_events(void)
{
v2_zero( vg.mouse_wheel );
{
if( event.type == SDL_KEYDOWN )
{
- if( vg_console.enabled &&
- (vg_ui.ctx.focused_control_type != k_ui_control_modal) )
+ if( vg_console.enabled && (vg_ui.ctx.focused_control_type != k_ui_control_modal) )
{
- if( event.key.keysym.sym == SDLK_ESCAPE ||
- event.key.keysym.scancode == SDL_SCANCODE_GRAVE )
+ if( (event.key.keysym.sym == SDLK_ESCAPE) || (event.key.keysym.scancode == SDL_SCANCODE_GRAVE) )
{
vg_console.enabled = 0;
ui_defocus_all( &vg_ui.ctx );
}
- else if( (event.key.keysym.mod & KMOD_CTRL) &&
- event.key.keysym.sym == SDLK_n )
+ else if( (event.key.keysym.mod & KMOD_CTRL) && (event.key.keysym.sym == SDLK_n) )
{
console_suggest_next( &vg_ui.ctx );
}
- else if( (event.key.keysym.mod & KMOD_CTRL ) &&
- event.key.keysym.sym == SDLK_p )
+ else if( (event.key.keysym.mod & KMOD_CTRL ) && (event.key.keysym.sym == SDLK_p) )
{
console_suggest_prev( &vg_ui.ctx );
}
vg.mouse_wheel[0] += event.wheel.preciseX;
vg.mouse_wheel[1] += event.wheel.preciseY;
}
- else if( event.type == SDL_CONTROLLERDEVICEADDED ||
- event.type == SDL_CONTROLLERDEVICEREMOVED )
+ else if( (event.type == SDL_CONTROLLERDEVICEADDED) || (event.type == SDL_CONTROLLERDEVICEREMOVED) )
{
vg_input_device_event( &event );
}
vg_lines.enabled = vg_lines.render;
vg.time_fixed_accumulator += vg.time_delta;
- while( vg.time_fixed_accumulator >= vg.time_fixed_delta ){
+ while( vg.time_fixed_accumulator >= vg.time_fixed_delta )
+ {
vg_fixed_update();
vg_lines.enabled = 0;
vg.time_fixed_accumulator -= vg.time_fixed_delta;
vg.fixed_iterations ++;
- if( vg.fixed_iterations == 8 ){
+ if( vg.fixed_iterations == 8 )
break;
- }
}
vg_lines.enabled = vg_lines.render;
vg.time_fixed_extrapolate = vg.time_fixed_accumulator / vg.time_fixed_delta;
}
}
-static int vg_framefilter( double dt ){
- if( vg.fps_limit < 24 ) vg.fps_limit = 24;
- if( vg.fps_limit > 300 ) vg.fps_limit = 300;
+static int vg_framefilter( f64 dt )
+{
+ if( vg.fps_limit < 24 )
+ vg.fps_limit = 24;
+
+ if( vg.fps_limit > 300 )
+ vg.fps_limit = 300;
double min_frametime = 1.0/(double)vg.fps_limit;
- if( vg.time_frame_delta < min_frametime ){
+ if( vg.time_frame_delta < min_frametime )
+ {
/* TODO: we can use high res nanosleep on Linux here */
double sleep_ms = (min_frametime-vg.time_frame_delta) * 1000.0;
u32 ms = (u32)floor( sleep_ms );
- if( ms ){
- if( !vg_loader_availible() )
- SDL_Delay(1);
- else
- SDL_Delay(ms);
- }
- else{
+ if( ms )
+ SDL_Delay(ms);
+ else
vg.time_spinning ++;
- }
return 1;
}
vg.time_hp_last = vg.time_hp;
double dt = (double)udt / (double)SDL_GetPerformanceFrequency();
-
vg.time_frame_delta += dt;
- vg_run_async_checked();
+
+ while( vg_async_has_work( &vg.main_tasks ) )
+ {
+ if( vg_async_process_next_task( &vg.main_tasks ) == 0 )
+ return;
+ }
if( vg_framefilter( dt ) )
continue;
_vg_process_events();
if( vg.window_should_close )
- break;
+ return;
if( status == k_engine_status_crashed )
{
return 0;
}
+static int _vg_loader_thread( void *pfn )
+{
+ SDL_TLSSet( vg.thread_purpose, &k_thread_purpose_loader, NULL );
+
+ if( setjmp( vg.env_loader_exit ) )
+ return 0;
+
+ while( vg_async_process_next_task( &vg.loader_tasks ) ) {}
+ return 0;
+}
+
static int cmd_vg_settings_toggle( int argc, const char *argv[] );
void vg_enter( int argc, char *argv[], const char *window_name )
{
+ if( !vg_init_async_queue( &vg.main_tasks ) )
+ return;
+ if( !vg_init_async_queue( &vg.loader_tasks ) )
+ return;
+
vg.thread_purpose = SDL_TLSCreate();
VG_ASSERT( vg.thread_purpose );
- SDL_TLSSet( vg.thread_purpose, &k_thread_purpose_nothing, NULL );
+
+ /* we pretend to be loader thread while it doesn't exist */
+ SDL_TLSSet( vg.thread_purpose, &k_thread_purpose_loader, NULL );
vg_rand_seed( &vg.rand, 461 );
_vg_process_launch_opts_internal( argc, argv );
vg_console_reg_cmd( "vg_rtmemory", cmd_log_memory, NULL );
_vg_init_window( window_name );
- vg_async_init();
SDL_SetRelativeMouseMode(1);
/* multi-threaded loading phase */
- vg.sem_loader = SDL_CreateSemaphore(1);
- VG_ASSERT( vg.sem_loader );
SDL_AtomicSet( &vg.engine_status, k_engine_status_load_internal );
- SDL_TLSSet( vg.thread_purpose, &k_thread_purpose_main, NULL );
/* Opengl-required systems */
vg_ui_init();
-
vg_loader_step( vg_loader_init, vg_loader_free );
- vg_loader_start( _vg_load_full, NULL );
+ vg_async_call( &vg.loader_tasks, _vg_load_step0, NULL );
+ SDL_TLSSet( vg.thread_purpose, &k_thread_purpose_main, NULL );
+ SDL_CreateThread( _vg_loader_thread, "vg: loader", NULL );
_vg_gameloop();
_vg_terminate();
}
__attribute__((used)) u32 NvOptimusEnablement = 0x00000001;
__attribute__((used)) int AmdPowerXpressRequestHighPerformance = 1;
-#include "vg_async.c"
+#include "vg_async2.c"
#include "vg_audio.c"
#include "vg_audio_dsp.c"
#include "vg_audio_synth_bird.c"
#include "vg_font.h"
#include "vg_string.h"
#include "vg_ui/imgui.h"
+#include "vg_async2.h"
#include <setjmp.h>
/* Engine sync */
SDL_Window *window;
SDL_GLContext gl_context;
- SDL_sem *sem_loader; /* allows only one loader at a time */
SDL_TLSID thread_purpose;
- //SDL_threadID thread_id_main,
- // thread_id_loader;
-
- jmp_buf env_loader_exit;
- void *thread_data;
+ vg_async_queue main_tasks, loader_tasks;
+ jmp_buf env_loader_exit; /* for deep fatal error handling */
SDL_atomic_t engine_status;
time_frame_delta;
f32 time_fixed_delta;
-
u64 time_hp, time_hp_last, time_spinning;
int fixed_iterations;
- enum engine_stage{
+ enum engine_stage
+ {
k_engine_stage_none,
k_engine_stage_update,
k_engine_stage_update_fixed,
extern const u32 k_thread_purpose_main;
extern const u32 k_thread_purpose_loader;
u32 vg_thread_purpose(void);
+void _vg_load_temp_end(void);
+
+#define THREAD_0 VG_ASSERT( vg_thread_purpose() == k_thread_purpose_main )
+#define THREAD_1 VG_ASSERT( vg_thread_purpose() == k_thread_purpose_loader )
void vg_checkgl( const char *src_info );
#define VG_STRINGIT( X ) #X
#include "vg_framebuffer.h"
#include "vg_platform.h"
-#include "vg_async.h"
struct
{
return fb;
}
-static void async_framebuffer_create( void *payload, u32 size )
+static void async_framebuffer_create( void *_fb )
{
- vg_framebuffer *fb = payload;
+ vg_framebuffer *fb = _fb;
glGenFramebuffers( 1, &fb->id );
glBindFramebuffer( GL_FRAMEBUFFER, fb->id );
void vg_framebuffer_create( vg_framebuffer *fb )
{
- vg_async_call( async_framebuffer_create, fb, 0 );
+ THREAD_1;
+ vg_async_call( &vg.main_tasks, async_framebuffer_create, fb );
}
void vg_framebuffer_free( vg_framebuffer *fb )
#include "vg_input.h"
#include "vg_loader.h"
#include "vg_engine.h"
-#include "vg_async.h"
#include "vg_string.h"
f32 controller_deadzone = 0.05f;
vg_input.hidden_mouse_travel = 0.0f;
}
-void async_vg_input_init( void *payload, u32 size )
+static void async_vg_input_init( void *_ )
{
vg_info( "Checking for controllers\n" );
SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
int joy_count = SDL_NumJoysticks();
- for( int i=0; i<joy_count; i++ ) {
+ for( int i=0; i<joy_count; i++ )
+ {
const char *name = SDL_JoystickNameForIndex( i );
int is_controller = SDL_IsGameController(i);
void vg_input_init(void)
{
+ THREAD_1;
VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
- vg_async_call( async_vg_input_init, NULL, 0 );
+ vg_async_call( &vg.main_tasks, async_vg_input_init, NULL );
}
void vg_input_free(void)
u8 vg_getkey( SDL_Keycode kc );
void vg_process_inputs(void);
-void async_vg_input_init( void *payload, u32 size );
void vg_input_init(void);
void vg_input_free(void);
struct vg_controller *vg_active_controller(void);
#include "vg_lines.h"
#include "vg_shader.h"
#include "vg_engine.h"
-#include "vg_async.h"
struct vg_lines vg_lines;
#define VG_LINES_BUFFER_SIZE 50000 * sizeof( struct vg_lines_vert )
-static void async_vg_lines_init( void *payload, u32 payload_size )
+static void async_vg_lines_init( void *_ )
{
+ THREAD_0;
+
glGenVertexArrays( 1, &vg_lines.vao );
glGenBuffers( 1, &vg_lines.vbo );
glBindVertexArray( vg_lines.vao );
void vg_lines_init(void)
{
- vg_lines.vertex_buffer =
- vg_create_linear_allocator( vg_mem.rtmemory,
- VG_LINES_BUFFER_SIZE, VG_MEMORY_REALTIME);
-
- vg_async_call( async_vg_lines_init, NULL, 0 );
+ THREAD_1;
- vg_console_reg_var( "vg_lines", &vg_lines.render, k_var_dtype_i32,
- VG_VAR_CHEAT );
+ vg_lines.vertex_buffer = vg_create_linear_allocator( vg_mem.rtmemory, VG_LINES_BUFFER_SIZE, VG_MEMORY_REALTIME);
+ vg_async_call( &vg.main_tasks, async_vg_lines_init, NULL );
+ vg_console_reg_var( "vg_lines", &vg_lines.render, k_var_dtype_i32, VG_VAR_CHEAT );
vg_shader_register( &_shader_lines );
}
#include "vg_engine.h"
#include "vg_loader.h"
#include "vg_shader.h"
-#include "vg_async.h"
struct vg_loader vg_loader;
void vg_loader_atexit(void)
{
- vg_info( "Shutdown steps\n" );
for( int i=0; i<vg_loader.step_count; i++ )
{
- struct loader_free_step *step =
- &vg_loader.step_buffer[vg_loader.step_count -1 -i];
-
- vg_info( " -> %p\n", step->fn_free );
+ struct loader_free_step *step = &vg_loader.step_buffer[vg_loader.step_count -1 -i];
step->fn_free();
}
}
const char *string;
};
-void async_loader_set_user_information( void *payload, u32 size )
+void async_loader_set_user_information( vg_async_task *task )
{
- struct async_set_information_args *args = payload;
+ struct async_set_information_args *args = (void *)task->data;
vg_loader.information_for_user = args->string;
}
{
if( vg_thread_purpose() == k_thread_purpose_loader )
{
- vg_async_item *call =
- vg_async_alloc( sizeof(struct async_set_information_args) );
-
- struct async_set_information_args *args = call->payload;
+ vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, sizeof(struct async_set_information_args), 1 );
+ struct async_set_information_args *args = (void *)task->data;
args->string = information;
-
- vg_async_dispatch( call, async_loader_set_user_information );
+ vg_async_task_dispatch( task, async_loader_set_user_information );
if( vg.load_step_delay )
SDL_Delay( vg.load_step_delay );
vg.loader_ring = 1.0f;
}
-static int _vg_loader_thread( void *pfn )
-{
- SDL_TLSSet( vg.thread_purpose, &k_thread_purpose_loader, NULL );
-
- if( setjmp( vg.env_loader_exit ) )
- return 0;
-
- /* Run client loader */
- void (*call_func)(void *data) = pfn;
- call_func( vg.thread_data );
-
- SDL_SemPost( vg.sem_loader );
- return 0;
-}
-
-int vg_loader_availible(void)
-{
- if( SDL_SemValue( vg.sem_loader ) )
- {
- if( !(vg_async.start) )
- return 1;
- }
-
- return 0;
-}
-
-void vg_loader_start( void(*pfn)(void *data), void *data )
-{
- SDL_SemWait( vg.sem_loader );
-
- vg.thread_data = data;
- SDL_CreateThread( _vg_loader_thread, "vg: loader", pfn );
-}
-
/*
* Schedule something to be ran now, freed later. Checks in with engine status
*/
-void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void),
- const char *alias )
+void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void), const char *alias )
{
+ THREAD_1;
+
u64 t0 = SDL_GetPerformanceCounter();
- vg.time_hp_last = vg.time_hp;
+ vg.time_hp_last = vg.time_hp; // ???
if( fn_load )
fn_load();
u64 udt = SDL_GetPerformanceCounter() - t0;
double dt = (double)udt / (double)SDL_GetPerformanceFrequency();
- vg_info( "ltime [%p] %s: %fs\n", fn_load, alias, dt );
+ vg_info( "%s: %fs\n", alias, dt );
if( fn_free )
{
/*
- * Copyright 2021-2024 (C) Mount0 Software, Harry Godden - All Rights Reserved
+ * Copyright 2021-2025 (C) Mount0 Software, Harry Godden - All Rights Reserved
* -----------------------------------------------------------------------------
*
* Splash / load screen
struct vg_loader
{
/* Shutdown steps */
- struct loader_free_step{
+ struct loader_free_step
+ {
void (*fn_free)(void);
}
step_buffer[16];
}
extern vg_loader;
-void vg_loader_start( void(*pfn)(void *data), void *data );
-void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void),
- const char *alias );
+//void vg_loader_start( void(*pfn)(void *data), void *data );
+void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void), const char *alias );
void vg_loader_set_user_information( const char *information );
-int vg_loader_availible(void);
void vg_loader_render(void);
void vg_loader_render_ring( f32 opacity );
void vg_loader_free(void);
void vg_loader_atexit(void);
void vg_loader_init(void);
+//int vg_loader_availible(void);
#define vg_loader_step( FN, FN_FREE )\
_vg_loader_step( FN, FN_FREE, #FN )
--- /dev/null
+#ifdef VG_ENGINE
+#define vg_semaphore SDL_sem *
+#define vg_mutex SDL_mutex *
+#define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (SEMAPHORE = SDL_CreateSemaphore( VALUE ))
+#define VG_SEMAPHORE_FREE( SEMAPHORE ) SDL_DestroySemaphore( SEMAPHORE ); SEMAPHORE = NULL;
+#define VG_MUTEX_INIT( MUTEX ) (MUTEX = SDL_CreateMutex())
+#define VG_MUTEX_FREE( MUTEX ) SDL_DestroyMutex( MUTEX ); MUTEX = NULL;
+#define VG_MUTEX_LOCK( MUTEX ) SDL_LockMutex( MUTEX )
+#define VG_MUTEX_UNLOCK( MUTEX ) SDL_UnlockMutex( MUTEX )
+#define VG_SEMAPHORE_WAIT( SEMAPHORE ) SDL_SemWait( SEMAPHORE )
+#define VG_SEMAPHORE_POST( SEMAPHORE ) SDL_SemPost( SEMAPHORE )
+#define VG_SEMAPHORE_VALUE( SEMAPHORE ) SDL_SemValue( SEMAPHORE )
+#else
+#include <pthread.h>
+#include <semaphore.h>
+#define vg_semaphore sem_t
+#define vg_mutex pthread_mutex_t
+#define VG_SEMAPHORE_INIT( SEMAPHORE, VALUE ) (sem_init( &SEMAPHORE, 0, VALUE )==0)
+#define VG_SEMAPHORE_FREE( SEMAPHORE ) sem_destroy( &SEMAPHORE )
+#define VG_SEMAPHORE_WAIT( SEMAPHORE ) sem_wait( &SEMAPHORE )
+#define VG_SEMAPHORE_POST( SEMAPHORE ) sem_post( &SEMAPHORE )
+static inline i32 get_semaphore_value( sem_t *sem )
+{
+ int value = 0;
+ sem_getvalue( sem, &value );
+ return value;
+}
+#define VG_SEMAPHORE_VALUE( SEMAPHORE ) get_semaphore_value( &SEMAPHORE )
+#define VG_MUTEX_INIT( MUTEX ) (pthread_mutex_init( &MUTEX, NULL )==0)
+#define VG_MUTEX_FREE( MUTEX ) pthread_mutex_destroy( &MUTEX )
+#define VG_MUTEX_LOCK( MUTEX ) pthread_mutex_lock( &MUTEX )
+#define VG_MUTEX_UNLOCK( MUTEX ) pthread_mutex_unlock( &MUTEX )
+#endif
+
.scale = 1.0f
};
-static void vg_async_postprocess_init( void *payload, u32 size )
+static void vg_async_postprocess_init( void *userdata )
{
+ THREAD_0;
+
f32 quad[] =
{
0.00f,0.00f, 1.00f,1.00f, 0.00f,1.00f,
void vg_render_init(void)
{
- vg_async_call( vg_async_postprocess_init, NULL, 0 );
+ THREAD_1;
+ vg_async_call( &vg.main_tasks, vg_async_postprocess_init, NULL );
vg_console_reg_var( "render_scale", &_vg_render.scale, k_var_dtype_f32, VG_VAR_PERSISTENT );
#ifdef VG_3D
#include "vg_rigidbody.h"
#include "vg_shader.h"
#include "vg_engine.h"
-#include "vg_async.h"
static struct vg_shader _shader_rigidbody =
{
u16 *tris;
};
-static void async_vg_rb_view_init( void *payload, u32 payload_size )
+static void async_vg_rb_view_init( vg_async_task *task )
{
- struct vg_rb_mesh_init *inf = payload;
+ struct vg_rb_mesh_init *inf = (void *)task->data;
glGenVertexArrays( 1, &vg_rb_view.vao );
glGenBuffers( 1, &vg_rb_view.vbo );
void vg_rb_view_init(void)
{
+ THREAD_1;
+
vg_shader_register( &_shader_rigidbody );
u32 H = 20,
vert_size = vg_align8( verts_count * sizeof(rb_view_vert) ),
tris_size = vg_align8( tris_count * 3 * sizeof(u16) );
- vg_async_item *call = vg_async_alloc( hdr_size + vert_size + tris_size );
-
- struct vg_rb_mesh_init *inf = call->payload;
+ vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, hdr_size + vert_size + tris_size, 1 );
+ struct vg_rb_mesh_init *inf = (void *)task->data;
rb_view_vert *verts = ((void *)inf) + hdr_size;
u16 *tris = ((void *)inf) + hdr_size + vert_size;
tri_index += 1;
}
- vg_async_dispatch( call, async_vg_rb_view_init );
+ vg_async_task_dispatch( task, async_vg_rb_view_init );
}
void vg_rb_view_bind(void)
void vg_auto_shader_link(void);
#endif
-void vg_shaders_compile(void)
+void vg_shaders_compile(void *_)
{
- vg_info( "Compiling shaders\n" );
+ THREAD_0;
+ vg_info( "Compiling shaders\n" );
for( int i=0; i<vg_shaders.count; i ++ )
vg_compile_shader( vg_shaders.shaders[i] );
#ifdef VG_CUSTOM_SHADERS
vg_auto_shader_link();
#endif
-
return 0;
}
int compiled;
};
-void vg_shaders_compile(void);
+void vg_shaders_compile(void *_);
int vg_shaders_live_recompile(int argc, const char *argv[]);
void vg_shader_register( struct vg_shader *shader );
void vg_compile_shader( struct vg_shader *shader );
#include "vg_tex.h"
#include "vg_engine.h"
-#include "vg_async.h"
#include "vg_io.h"
#include <string.h>
bytes[(*p)++] = (0x000000ff & v);
}
-struct texture_load_info{
+struct texture_load_info
+{
GLuint *dest;
u32 width, height, flags;
u8 *rgba;
};
-static void async_vg_tex2d_upload( void *payload, u32 size )
+static void async_vg_tex2d_upload( vg_async_task *task )
{
- VG_ASSERT( vg_thread_purpose() == k_thread_purpose_main );
-
- struct texture_load_info *info = payload;
+ THREAD_0;
+ struct texture_load_info *info = (void *)task->data;
glGenTextures( 1, info->dest );
void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest )
{
- u32 hdr_size = vg_align8(sizeof(struct texture_load_info));
+ THREAD_1;
- vg_async_item *call = vg_async_alloc( hdr_size );
- struct texture_load_info *info = call->payload;
-
+ vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, sizeof(struct texture_load_info), 1 );
+ struct texture_load_info *info = (void *)task->data;
info->dest = dest;
info->flags = VG_TEX2D_ERROR|VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP;
info->width = 4;
info->height = 4;
info->rgba = const_vg_tex2d_err;
-
- vg_async_dispatch( call, async_vg_tex2d_upload );
+ vg_async_task_dispatch( task, async_vg_tex2d_upload );
}
void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, u32 flags, GLuint *dest )
{
+ THREAD_1;
+
u32 header_magic;
qoi_rgba_t index[64];
qoi_rgba_t px;
u32 hdr_size = vg_align8(sizeof(struct texture_load_info)),
tex_size = vg_align8(px_len);
- vg_async_item *call = vg_async_alloc( hdr_size + tex_size );
- struct texture_load_info *info = call->payload;
-
+ vg_async_task *task = vg_allocate_async_task( &vg.main_tasks, hdr_size + tex_size, 1 );
+ struct texture_load_info *info = (void *)task->data;
info->dest = dest;
info->flags = flags;
info->width = desc.width;
info->height = desc.height;
- info->rgba = ((u8*)call->payload) + hdr_size;
+ info->rgba = task->data + hdr_size;
/*
* Decode
* --------------------------
*/
- vg_async_dispatch( call, async_vg_tex2d_upload );
+ vg_async_task_dispatch( task, async_vg_tex2d_upload );
}
void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest )