From: hgn Date: Tue, 15 Apr 2025 20:42:26 +0000 (+0100) Subject: stuff X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;h=63c7e5970286df5c5c17a4669935ab5b5894e962;p=vg.git stuff --- diff --git a/vg_async.c b/vg_async.c deleted file mode 100644 index c071641..0000000 --- a/vg_async.c +++ /dev/null @@ -1,144 +0,0 @@ -#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 ); -} diff --git a/vg_async.h b/vg_async.h deleted file mode 100644 index d20cccf..0000000 --- a/vg_async.h +++ /dev/null @@ -1,61 +0,0 @@ -/* 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); diff --git a/vg_async2.c b/vg_async2.c index 925166c..926cce6 100644 --- a/vg_async2.c +++ b/vg_async2.c @@ -2,16 +2,16 @@ 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 ); @@ -20,100 +20,132 @@ bool vg_init_async_queue( vg_async_queue *queue ) 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 ); } diff --git a/vg_async2.h b/vg_async2.h index 1e6f2d0..22c0e6c 100644 --- a/vg_async2.h +++ b/vg_async2.h @@ -1,9 +1,7 @@ #pragma once #include "vg/vg_mem.h" #include "vg/vg_mem_queue.h" - -#include -#include +#include "vg/vg_mutex.h" typedef struct vg_async_queue vg_async_queue; typedef struct vg_async_task vg_async_task; @@ -13,10 +11,12 @@ struct vg_async_queue 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 { @@ -29,6 +29,8 @@ struct vg_async_queue struct vg_async_task { + vg_async_queue *queue; + const char *alloc_debug_info; void (*handler)( vg_async_task *task ); u8 data[]; }; @@ -37,10 +39,13 @@ bool vg_init_async_queue( vg_async_queue *queue ); 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 ); diff --git a/vg_audio_dsp.c b/vg_audio_dsp.c index cd49883..4a3536e 100644 --- a/vg_audio_dsp.c +++ b/vg_audio_dsp.c @@ -1,6 +1,5 @@ #include "vg_audio_dsp.h" #include "vg_mem.h" -#include "vg_async.h" struct vg_dsp vg_dsp; diff --git a/vg_build.h b/vg_build.h index 9a1d861..754b2d4 100644 --- a/vg_build.h +++ b/vg_build.h @@ -278,7 +278,7 @@ vg_compiler_run( struct vg_project *project, 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" ); diff --git a/vg_engine.c b/vg_engine.c index 82cea76..af6f137 100644 --- a/vg_engine.c +++ b/vg_engine.c @@ -1,10 +1,19 @@ #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; @@ -62,35 +71,24 @@ struct vg_profile_set static _vg_prof_gameloop = } }; -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" ); @@ -101,7 +99,18 @@ vg_info(" ' ' '--' [] '----- '----- ' ' '---' " 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 */ @@ -123,12 +132,26 @@ vg_info(" ' ' '--' [] '----- '----- ' ' '---' " 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 ); @@ -140,22 +163,18 @@ static void _vg_process_events(void) { 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 ); } @@ -182,8 +201,7 @@ static void _vg_process_events(void) 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 ); } @@ -250,15 +268,15 @@ static void _vg_gameloop_update(void) 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; @@ -361,25 +379,25 @@ static void vg_changevsync(void) } } -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; } @@ -430,9 +448,13 @@ static void _vg_gameloop(void) 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; @@ -455,7 +477,7 @@ static void _vg_gameloop(void) _vg_process_events(); if( vg.window_should_close ) - break; + return; if( status == k_engine_status_crashed ) { @@ -691,12 +713,30 @@ static int cmd_log_memory( int argc, const char *argv[] ) 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 ); @@ -718,21 +758,18 @@ void vg_enter( int argc, char *argv[], const char *window_name ) 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(); } @@ -1267,7 +1304,7 @@ static int cmd_vg_settings_toggle( int argc, const char *argv[] ) __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" diff --git a/vg_engine.h b/vg_engine.h index 15ea85e..1b977ca 100644 --- a/vg_engine.h +++ b/vg_engine.h @@ -80,6 +80,7 @@ #include "vg_font.h" #include "vg_string.h" #include "vg_ui/imgui.h" +#include "vg_async2.h" #include @@ -124,15 +125,11 @@ struct vg_engine /* 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; @@ -174,12 +171,12 @@ struct vg_engine 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, @@ -259,6 +256,10 @@ extern const u32 k_thread_purpose_nothing; 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 diff --git a/vg_framebuffer.c b/vg_framebuffer.c index 2f4a68f..9cc045c 100644 --- a/vg_framebuffer.c +++ b/vg_framebuffer.c @@ -1,6 +1,5 @@ #include "vg_framebuffer.h" #include "vg_platform.h" -#include "vg_async.h" struct { @@ -249,9 +248,9 @@ vg_framebuffer *vg_framebuffer_allocate( void *alloc, u32 attachment_count, bool 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 ); @@ -331,7 +330,8 @@ static void async_framebuffer_create( void *payload, u32 size ) 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 ) diff --git a/vg_input.c b/vg_input.c index 9ca03a4..3a1aa27 100644 --- a/vg_input.c +++ b/vg_input.c @@ -3,7 +3,6 @@ #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; @@ -257,13 +256,14 @@ void vg_process_inputs(void) 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 %p\n", step->fn_free ); + struct loader_free_step *step = &vg_loader.step_buffer[vg_loader.step_count -1 -i]; step->fn_free(); } } @@ -105,9 +100,9 @@ struct async_set_information_args 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; } @@ -115,13 +110,10 @@ void vg_loader_set_user_information( const char *information ) { 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 ); @@ -173,55 +165,22 @@ void vg_loader_render(void) 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 ) { diff --git a/vg_loader.h b/vg_loader.h index 3d0cbef..8f43b3a 100644 --- a/vg_loader.h +++ b/vg_loader.h @@ -1,6 +1,6 @@ /* - * 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 @@ -16,7 +16,8 @@ struct vg_loader { /* Shutdown steps */ - struct loader_free_step{ + struct loader_free_step + { void (*fn_free)(void); } step_buffer[16]; @@ -28,16 +29,15 @@ struct vg_loader } 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 ) diff --git a/vg_mutex.h b/vg_mutex.h new file mode 100644 index 0000000..7e9974c --- /dev/null +++ b/vg_mutex.h @@ -0,0 +1,34 @@ +#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 +#include +#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 + diff --git a/vg_render.c b/vg_render.c index 477d470..7c63978 100644 --- a/vg_render.c +++ b/vg_render.c @@ -47,8 +47,10 @@ struct vg_render _vg_render = .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, @@ -68,8 +70,9 @@ static void vg_async_postprocess_init( void *payload, u32 size ) 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 diff --git a/vg_rigidbody_view.c b/vg_rigidbody_view.c index f16f3c6..f03f1c4 100644 --- a/vg_rigidbody_view.c +++ b/vg_rigidbody_view.c @@ -3,7 +3,6 @@ #include "vg_rigidbody.h" #include "vg_shader.h" #include "vg_engine.h" -#include "vg_async.h" static struct vg_shader _shader_rigidbody = { @@ -109,9 +108,9 @@ struct vg_rb_mesh_init { 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 ); @@ -138,6 +137,8 @@ static void async_vg_rb_view_init( void *payload, u32 payload_size ) void vg_rb_view_init(void) { + THREAD_1; + vg_shader_register( &_shader_rigidbody ); u32 H = 20, @@ -160,9 +161,8 @@ void vg_rb_view_init(void) 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; @@ -247,7 +247,7 @@ void vg_rb_view_init(void) 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) diff --git a/vg_shader.c b/vg_shader.c index 3441207..b368183 100644 --- a/vg_shader.c +++ b/vg_shader.c @@ -197,10 +197,11 @@ void vg_free_shader( struct vg_shader *shader ) 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 @@ -72,17 +71,17 @@ static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { 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 ); @@ -148,11 +147,10 @@ static void async_vg_tex2d_upload( void *payload, u32 size ) 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; @@ -162,12 +160,13 @@ void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest ) 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; @@ -218,14 +217,13 @@ void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, u32 flags, GLuint *dest 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 @@ -295,7 +293,7 @@ void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, u32 flags, GLuint *dest * -------------------------- */ - 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 )