From: hgn Date: Mon, 24 Apr 2023 11:47:51 +0000 (+0100) Subject: switch to async system X-Git-Url: https://harrygodden.com/git/?a=commitdiff_plain;ds=sidebyside;h=651edda3736812c89f43c11319c6b485ff14ea19;p=vg.git switch to async system --- diff --git a/submodules/SDL b/submodules/SDL index 06492c5..eef4d3c 160000 --- a/submodules/SDL +++ b/submodules/SDL @@ -1 +1 @@ -Subproject commit 06492c598158cf825a18aececaf7511d7fd04f48 +Subproject commit eef4d3c86a653f91b7221c80809ba8ab56f94cf1 diff --git a/vg.h b/vg.h index 7ddd04e..9ad04b1 100644 --- a/vg.h +++ b/vg.h @@ -45,18 +45,6 @@ |IMP| | vg_ui(void) | | | | | | '----' -'___' - - .-. -| ? | -| | .-------------------------------------. -|API| | vg_fatal_exit_loop( const char *err ) | -| | '-------------------------------------' -| | | -| | .------+. -| | | | -| | | v -|IMP| '- vg_framebuffer_resize(void) '___' */ @@ -106,8 +94,8 @@ VG_STATIC void vg_print_backtrace(void) #include "vg_m.h" #include "vg_io.h" #include "vg_log.h" - #include "vg_async.h" #include "vg_steam.h" + #include //#define VG_SYNC_DEBUG #ifdef VG_SYNC_DEBUG @@ -142,27 +130,22 @@ struct vg SDL_Window *window; SDL_GLContext gl_context; - SDL_SpinLock sl_context; - SDL_sem *sem_allow_exec, - *sem_exec_finished, - *sem_loader; + SDL_sem *sem_loader; /* allows only one loader at a time */ - SDL_threadID thread_id_main, - thread_id_loader, - thread_id_with_opengl_context; - int context_ownership_depth; + jmp_buf env_loader_exit; - int exec_context; + SDL_threadID thread_id_main, + thread_id_loader; + SDL_SpinLock sl_status; enum engine_status { k_engine_status_none, + k_engine_status_load_internal, k_engine_status_running, k_engine_status_crashed } engine_status; - const char *str_const_engine_err; - int is_loaded; /* Window information */ int window_x, @@ -227,132 +210,33 @@ enum vg_thread_purpose k_thread_purpose_loader }; -VG_STATIC void vg_fatal_exit_loop( const char *error ); +#include "vg_async.h" -/* - * Checks if the engine is running - */ -VG_STATIC void _vg_ensure_engine_running(void) +VG_STATIC enum engine_status _vg_engine_status(void) { - /* Check if the engine is no longer running */ - SDL_AtomicLock( &vg.sl_context ); + SDL_AtomicLock( &vg.sl_status ); enum engine_status status = vg.engine_status; - SDL_AtomicUnlock( &vg.sl_context ); + SDL_AtomicUnlock( &vg.sl_status ); - if( status != k_engine_status_running ){ - while(1) { - VG_SYNC_LOG( "[%d] No longer running...\n"); - SDL_Delay(1000); - } - } + return status; } VG_STATIC enum vg_thread_purpose vg_thread_purpose(void) { - SDL_AtomicLock( &vg.sl_context ); + SDL_AtomicLock( &vg.sl_status ); if( vg.thread_id_main == SDL_GetThreadID(NULL) ){ - SDL_AtomicUnlock( &vg.sl_context ); + SDL_AtomicUnlock( &vg.sl_status ); return k_thread_purpose_main; } else{ - SDL_AtomicUnlock( &vg.sl_context ); + SDL_AtomicUnlock( &vg.sl_status ); return k_thread_purpose_loader; } } -/* - * Sync execution so that the OpenGL context is switched onto this thread. - * Anything after this call will be in a valid context. - */ -VG_STATIC void vg_acquire_thread_sync(void) -{ - /* We dont want to do anything if this is the main thread */ - - if( vg_thread_purpose() == k_thread_purpose_loader ){ - VG_SYNC_LOG( "[%d] vg_acquire_thread_sync()\n" ); - _vg_ensure_engine_running(); - - SDL_AtomicLock( &vg.sl_context ); - if( vg.context_ownership_depth == 0 ){ - vg.context_ownership_depth ++; - vg.exec_context = 1; - SDL_AtomicUnlock( &vg.sl_context ); - - /* wait until told we can go */ - VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n" ); - SDL_SemWait( vg.sem_allow_exec ); - - _vg_ensure_engine_running(); - - SDL_GL_MakeCurrent( vg.window, vg.gl_context ); - VG_SYNC_LOG( "[%d] granted\n" ); - } - else{ - vg.context_ownership_depth ++; - VG_SYNC_LOG( "[%d] granted\n" ); - SDL_AtomicUnlock( &vg.sl_context ); - } - } -} - -/* - * Signify that we are done with the OpenGL context in this thread. - * Anything after this call will be in an undefined context. - */ -VG_STATIC void vg_release_thread_sync(void) -{ - if( vg_thread_purpose() == k_thread_purpose_loader ){ - VG_SYNC_LOG( "[%d] vg_release_thread_sync()\n" ); - - SDL_AtomicLock( &vg.sl_context ); - vg.context_ownership_depth --; - - if( vg.context_ownership_depth == 0 ){ - SDL_AtomicUnlock( &vg.sl_context ); - VG_SYNC_LOG( "[%d] Releasing context.\n" ); - SDL_GL_MakeCurrent( NULL, NULL ); - SDL_SemPost( vg.sem_exec_finished ); - } - else - SDL_AtomicUnlock( &vg.sl_context ); - } -} - -VG_STATIC void _vg_run_synced(void) -{ - SDL_AtomicLock( &vg.sl_context ); - - if( vg.exec_context != 0 ){ - VG_SYNC_LOG( "[%d] _vg_run_synced() (%d).\n", vg.exec_context ); - vg.exec_context = 0; - SDL_AtomicUnlock( &vg.sl_context ); - - /* allow operations to go */ - SDL_GL_MakeCurrent( NULL, NULL ); - SDL_SemPost( vg.sem_allow_exec ); - - /* wait for operations to complete */ - VG_SYNC_LOG( "[%d] Waiting for content.\n" ); - SDL_SemWait( vg.sem_exec_finished ); - - /* check if we killed the engine */ - _vg_ensure_engine_running(); - - /* re-engage main thread */ - VG_SYNC_LOG( "[%d] Re-engaging.\n" ); - SDL_GL_MakeCurrent( vg.window, vg.gl_context ); - } - else{ - VG_SYNC_LOG( "[%d] Nothing to do.\n" ); - SDL_AtomicUnlock( &vg.sl_context ); - } -} - VG_STATIC void _vg_opengl_sync_init(void) { - vg.sem_allow_exec = SDL_CreateSemaphore(0); - vg.sem_exec_finished = SDL_CreateSemaphore(0); vg.sem_loader = SDL_CreateSemaphore(1); } @@ -387,21 +271,36 @@ VG_STATIC void vg_checkgl( const char *src_info ) } if( fail ) - vg_fatal_exit_loop( "OpenGL Error" ); + vg_fatal_error( "OpenGL Error" ); +} + +VG_STATIC void async_vg_bake_shaders( void *payload, u32 size ) +{ + vg_shaders_compile(); } VG_STATIC void vg_bake_shaders(void) { - vg_acquire_thread_sync(); vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL ); - vg_shaders_compile(); - vg_release_thread_sync(); + vg_async_item *call = vg_async_alloc(0); + vg_async_dispatch( call, async_vg_bake_shaders ); } -void test_async_runner( void *payload, u32 size ) +void async_internal_complete( void *payload, u32 size ) { - vg_success( "Async call test (%p, %u)\n", payload, size ); + vg_success( "Internal async setup complete\n" ); + SDL_AtomicLock( &vg.sl_status ); + + if( vg.engine_status == k_engine_status_crashed ){ + SDL_AtomicUnlock( &vg.sl_status ); + return; + } + else{ + vg.engine_status = k_engine_status_running; + } + + SDL_AtomicUnlock( &vg.sl_status ); } VG_STATIC void _vg_load_full(void) @@ -414,16 +313,15 @@ VG_STATIC void _vg_load_full(void) vg_loader_step( vg_audio_init, vg_audio_free ); vg_loader_step( vg_profiler_init, NULL ); + vg_async_item *test_async = vg_async_alloc( 0 ); + vg_async_dispatch( test_async, async_internal_complete ); + /* client */ vg_load(); - - vg_async_item *test_async = vg_async_alloc( 0 ); - vg_async_dispatch( test_async, test_async_runner ); } VG_STATIC void _vg_process_events(void) { - /* Update timers */ v2_zero( vg.mouse_wheel ); v2_zero( vg.mouse_delta ); @@ -520,66 +418,64 @@ VG_STATIC void _vg_gameloop_render(void) { vg_profile_begin( &vg_prof_render ); - if( vg.is_loaded ){ - /* render */ - vg.engine_stage = k_engine_stage_rendering; - vg_render(); + /* render */ + vg.engine_stage = k_engine_stage_rendering; + vg_render(); - /* ui */ - vg.engine_stage = k_engine_stage_ui; - { - ui_begin( vg.window_x, vg.window_y ); + /* ui */ + vg.engine_stage = k_engine_stage_ui; + { + ui_begin( vg.window_x, vg.window_y ); - /* TODO */ - ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 ); + /* TODO */ + ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 ); - int frame_target = vg.display_refresh_rate; + int frame_target = vg.display_refresh_rate; - if( vg.fps_limit > 0 ){ - frame_target = vg.fps_limit; - } + if( vg.fps_limit > 0 ){ + frame_target = vg.fps_limit; + } + + vg_profile_drawn( + (struct vg_profile *[]){ + &vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3, + (1.0f/(float)frame_target)*1000.0f, + (ui_rect){ 4, 4, 250, 0 }, 0 + ); + + if( vg_profiler ){ + char perf[256]; - vg_profile_drawn( - (struct vg_profile *[]){ - &vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3, - (1.0f/(float)frame_target)*1000.0f, - (ui_rect){ 4, 4, 250, 0 }, 0 - ); - - if( vg_profiler ){ - char perf[256]; - - snprintf( perf, 255, - "x: %d y: %d\n" - "refresh: %d (%.1fms)\n" - "samples: %d\n" - "iterations: %d (acc: %.3fms%%)\n" - "time: real(%.2f) delta(%.2f) rate(%.2f)\n" + snprintf( perf, 255, + "x: %d y: %d\n" + "refresh: %d (%.1fms)\n" + "samples: %d\n" + "iterations: %d (acc: %.3fms%%)\n" + "time: real(%.2f) delta(%.2f) rate(%.2f)\n" #ifdef _WIN32 - " extrap(%.2f) frame(%.2f) spin( %llu )\n", + " extrap(%.2f) frame(%.2f) spin( %llu )\n", #else - " extrap(%.2f) frame(%.2f) spin( %lu )\n", + " extrap(%.2f) frame(%.2f) spin( %lu )\n", #endif - vg.window_x, vg.window_y, - frame_target, (1.0f/(float)frame_target)*1000.0f, - vg.samples, - vg.fixed_iterations, - (vg.time_fixed_accumulator/VG_TIMESTEP_FIXED)*100.0f, - vg.time, vg.time_delta, vg.time_rate, - vg.time_fixed_extrapolate, vg.time_frame_delta, - vg.time_spinning ); - - ui_text( (ui_rect){258, 4+24+12+12,0,0},perf, 1,0); - } - - /* FIXME */ - audio_debug_ui( vg.pv ); - vg_ui(); - _vg_console_draw(); - - ui_resolve(); - ui_draw( NULL ); + vg.window_x, vg.window_y, + frame_target, (1.0f/(float)frame_target)*1000.0f, + vg.samples, + vg.fixed_iterations, + (vg.time_fixed_accumulator/VG_TIMESTEP_FIXED)*100.0f, + vg.time, vg.time_delta, vg.time_rate, + vg.time_fixed_extrapolate, vg.time_frame_delta, + vg.time_spinning ); + + ui_text( (ui_rect){258, 4+24+12+12,0,0},perf, 1,0); } + + /* FIXME */ + audio_debug_ui( vg.pv ); + vg_ui(); + _vg_console_draw(); + + ui_resolve(); + ui_draw( NULL ); } vg_profile_end( &vg_prof_render ); @@ -651,6 +547,29 @@ VG_STATIC int vg_framefilter( double dt ) return 0; } +VG_STATIC int _vg_crashscreen(void) +{ + if( vg.window_should_close ) + return 1; + + if( vg_getkey( SDLK_ESCAPE ) ) + return 1; + + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); + glBlendEquation(GL_FUNC_ADD); + + glClearColor( 0.15f + sinf(vg.time)*0.1f, 0.0f, 0.0f,1.0f ); + glClear( GL_COLOR_BUFFER_BIT ); + glViewport( 0,0, vg.window_x, vg.window_y ); + + _vg_render_log(); + + return 0; +} + VG_STATIC void _vg_gameloop(void) { //vg.time_fixed_accumulator = 0.75f * (1.0f/60.0f); @@ -674,30 +593,32 @@ VG_STATIC void _vg_gameloop(void) vg_profile_begin( &vg_prof_swap ); SDL_GL_SwapWindow( vg.window ); - _vg_run_synced(); vg_profile_end( &vg_prof_swap ); - vg.time_delta = vg.time_frame_delta * vg.time_rate; - vg.time += vg.time_delta; - - vg_run_async_checked(); - _vg_process_events(); + enum engine_status status = _vg_engine_status(); - if( vg.window_should_close ) - break; - - if( vg.is_loaded ){ - if( !post_start ){ - vg_start(); - post_start = 1; - } + if( status == k_engine_status_crashed ){ + if( _vg_crashscreen() ) + break; } else{ - _vg_loader_render(); - } + vg.time_delta = vg.time_frame_delta * vg.time_rate; + vg.time += vg.time_delta; - _vg_gameloop_update(); - _vg_gameloop_render(); + vg_run_async_checked(); + _vg_process_events(); + + if( vg.window_should_close ) + break; + + if( status == k_engine_status_running ){ + _vg_gameloop_update(); + _vg_gameloop_render(); + } + else{ + _vg_loader_render(); + } + } vg.time_frame_delta = 0.0; vg.time_spinning = 0; @@ -872,6 +793,24 @@ VG_STATIC void _vg_init_window( const char *window_name ) #endif } +VG_STATIC void _vg_terminate(void) +{ + /* Shutdown */ + _vg_console_write_persistent(); + + SDL_AtomicLock( &vg.sl_status ); + vg.engine_status = k_engine_status_none; + SDL_AtomicUnlock( &vg.sl_status ); + + _vg_loader_free(); + + vg_success( "If you see this it means everything went.. \"well\".....\n" ); + + SDL_GL_DeleteContext( vg.gl_context ); + SDL_Quit(); + exit(0); +} + VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name ) { _vg_process_launch_opts_internal( argc, argv ); @@ -892,110 +831,45 @@ VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name ) _vg_ui_init(); _vg_loader_init(); - vg.engine_status = k_engine_status_running; + vg.engine_status = k_engine_status_load_internal; _vg_opengl_sync_init(); vg_loader_start( _vg_load_full ); _vg_gameloop(); - - /* Shutdown */ - _vg_console_write_persistent(); - - SDL_AtomicLock( &vg.sl_context ); - vg.engine_status = k_engine_status_none; - SDL_AtomicUnlock( &vg.sl_context ); - - _vg_loader_free(); - - vg_success( "If you see this it means everything went.. \"well\".....\n" ); - - SDL_GL_DeleteContext( vg.gl_context ); - SDL_Quit(); + _vg_terminate(); } -/* - * Immediately transfer away from calling thread into a safe loop, signal for - * others to shutdown, then free everything once the user closes the window. - * - * FIXME(bug): glfwWindowShouldClose() never returns 1 in windows via wine, ONLY - * when calling the program from outside its normal directory. - */ -VG_STATIC void vg_fatal_exit_loop( const char *error ) +VG_STATIC void vg_fatal_error( const char *fmt, ... ) { - /* - * https://www.gnu.org/software/libc/manual/html_node/Backtraces.html - * thanks gnu <3 - * - * TODO: this on windows? - */ + va_list args; + va_start( args, fmt ); + _vg_log_write( stderr, KRED " fatal" KWHT "| " KRED, fmt, args ); + va_end( args ); vg_print_backtrace(); - vg_error( "Fatal error: %s\n", error ); - SDL_AtomicLock( &vg.sl_context ); - if( vg.engine_status == k_engine_status_none ){ - SDL_AtomicUnlock( &vg.sl_context ); + SDL_AtomicLock( &vg.sl_status ); + vg.engine_status = k_engine_status_crashed; + SDL_AtomicUnlock( &vg.sl_status ); - /* TODO: Correct shutdown before other systems */ - exit(0); + if( vg_thread_purpose() == k_thread_purpose_loader ){ + longjmp( vg.env_loader_exit, 1 ); } else{ - SDL_AtomicUnlock( &vg.sl_context ); - - vg_acquire_thread_sync(); - - SDL_AtomicLock( &vg.sl_context ); - vg.engine_status = k_engine_status_crashed; - vg.str_const_engine_err = error; - SDL_AtomicUnlock( &vg.sl_context ); - - /* Notify other thread for curtosey */ - if( vg_thread_purpose() == k_thread_purpose_main ){ - SDL_AtomicLock( &vg.sl_context ); - - if( vg.exec_context != 0 ) - SDL_SemPost( vg.sem_allow_exec ); - - SDL_AtomicUnlock( &vg.sl_context ); - } - - vg_audio_free(); - - while(1){ - _vg_process_events(); - if( vg.window_should_close ) - break; - - if( vg_getkey( SDLK_ESCAPE ) ) - break; - - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); - glBlendEquation(GL_FUNC_ADD); - - glClearColor( 0.15f + sinf(vg.time)*0.1f, 0.0f, 0.0f,1.0f ); - glClear( GL_COLOR_BUFFER_BIT ); - glViewport( 0,0, vg.window_x, vg.window_y ); - - _vg_render_log(); - SDL_GL_SwapWindow( vg.window ); - } - - /* Can now shutdown and EXIT */ - _vg_loader_free(); - SDL_GL_DeleteContext( vg.gl_context ); - SDL_Quit(); - exit(0); + vg_error( "There is no jump to the error runner thing yet! bai bai\n" ); + _vg_terminate(); } } #else /* VG_GAME */ -VG_STATIC void vg_fatal_exit_loop( const char *error ) +#include "vg_log.h" +VG_STATIC void vg_fatal_error( const char *fmt, ... ) { - vg_error( "Fatal error: %s\n", error ); + va_list args; + va_start( args, fmt ); + _vg_log_write( stderr, KRED " fatal" KWHT "| " KRED, fmt, args ); + va_end( args ); exit(0); } diff --git a/vg_async.h b/vg_async.h index 366f448..190b5a8 100644 --- a/vg_async.h +++ b/vg_async.h @@ -7,6 +7,7 @@ #ifndef VG_ASYNC_H #define VG_ASYNC_H +#define VG_GAME #include "vg/vg.h" typedef struct vg_async_item vg_async_item; @@ -30,11 +31,21 @@ struct vg_async{ } static vg_async; +VG_STATIC enum vg_thread_purpose vg_thread_purpose(void); +VG_STATIC enum engine_status _vg_engine_status(void); + /* * Allocate an asynchronous call with a bit of memory */ VG_STATIC vg_async_item *vg_async_alloc( u32 size ) { + /* ditch out here if engine crashed. this serves as the 'quit checking' */ + + if( _vg_engine_status() == k_engine_status_crashed ){ + assert( vg_thread_purpose() == k_thread_purpose_loader ); + longjmp( vg.env_loader_exit, 1 ); + } + SDL_AtomicLock( &vg_async.sl_index ); u32 total_allocation = vg_align8(size) + vg_align8(sizeof(vg_async_item)), @@ -46,7 +57,8 @@ VG_STATIC vg_async_item *vg_async_alloc( u32 size ) vg_error( "Requested: %umb. Buffer size: %umb\n", (total_allocation/1024)/1024, (capacity/1024)/1024 ); - vg_fatal_exit_loop( "async alloc invalid size\n" ); + + vg_fatal_error( "async alloc invalid size\n" ); } if( total_allocation > remaining ){ @@ -66,7 +78,10 @@ VG_STATIC vg_async_item *vg_async_alloc( u32 size ) vg_async_item *entry = block; entry->next = NULL; - entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item)); + + if( size ) entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item)); + else entry->payload = NULL; + entry->size = size; entry->fn_runner = NULL; @@ -83,12 +98,21 @@ VG_STATIC vg_async_item *vg_async_alloc( u32 size ) return entry; } +VG_STATIC void vg_async_stall(void) +{ + vg_info( "async_stall: %d\n", SDL_SemValue( vg_async.sem_wait_for_flush ) ); + SDL_SemWait( vg_async.sem_wait_for_flush ); +} + /* * Mark the call as being filled and ready to go */ VG_STATIC void vg_async_dispatch( vg_async_item *item, void (*runner)( void *payload, u32 size ) ) { + if( SDL_SemValue(vg_async.sem_wait_for_flush) ) + SDL_SemWait(vg_async.sem_wait_for_flush); + SDL_AtomicLock( &vg_async.sl_index ); item->fn_runner = runner; SDL_AtomicUnlock( &vg_async.sl_index ); @@ -119,12 +143,17 @@ VG_STATIC void vg_run_async_checked(void) } } else{ - break; + SDL_AtomicUnlock( &vg_async.sl_index ); + return; } /* TODO: if exceed max frametime.... */ } + if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){ + SDL_SemPost( vg_async.sem_wait_for_flush ); + } + SDL_AtomicUnlock( &vg_async.sl_index ); } @@ -132,7 +161,7 @@ VG_STATIC void vg_async_init(void) { vg_async.sem_wait_for_flush = SDL_CreateSemaphore(0); vg_async.buffer = vg_create_linear_allocator( NULL, 50*1024*1024, - VG_MEMORY_REALTIME ); + VG_MEMORY_SYSTEM ); } #endif /* VG_ASYNC_H */ diff --git a/vg_audio.h b/vg_audio.h index cf6651e..34bc9ee 100644 --- a/vg_audio.h +++ b/vg_audio.h @@ -296,7 +296,7 @@ VG_STATIC void vg_audio_init(void) SDL_PauseAudioDevice( vg_audio.sdl_output_device, 0 ); } else{ - vg_fatal_exit_loop( + vg_fatal_error( "SDL_OpenAudioDevice failed. Your default audio device must support:\n" " Frequency: 44100 hz\n" " Buffer size: 512\n" @@ -840,10 +840,10 @@ static void audio_channel_mix( audio_channel *ch, float *buffer ) } } - if( !vg_validf( framevol_l ) ) vg_fatal_exit_loop( "NaN left channel" ); - if( !vg_validf( framevol_r ) ) vg_fatal_exit_loop( "NaN right channel" ); + if( !vg_validf( framevol_l ) ) vg_fatal_error( "NaN left channel" ); + if( !vg_validf( framevol_r ) ) vg_fatal_error( "NaN right channel" ); if( !vg_validf( frame_samplerate ) ) - vg_fatal_exit_loop( "NaN sample rate" ); + vg_fatal_error( "NaN sample rate" ); } u32 buffer_length = AUDIO_MIX_FRAME_SIZE; @@ -1146,7 +1146,7 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) if( format == k_audio_format_vorbis ){ if( !clip->path ){ - vg_fatal_exit_loop( "No path specified, embeded vorbis unsupported" ); + vg_fatal_error( "No path specified, embeded vorbis unsupported" ); } audio_lock(); @@ -1154,17 +1154,17 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) audio_unlock(); if( !clip->data ) - vg_fatal_exit_loop( "Audio failed to load" ); + vg_fatal_error( "Audio failed to load" ); float mb = (float)(clip->size) / (1024.0f*1024.0f); vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); } else if( format == k_audio_format_stereo ){ - vg_fatal_exit_loop( "Unsupported format (Stereo uncompressed)" ); + vg_fatal_error( "Unsupported format (Stereo uncompressed)" ); } else if( format == k_audio_format_bird ){ if( !clip->data ){ - vg_fatal_exit_loop( "No data, external birdsynth unsupported" ); + vg_fatal_error( "No data, external birdsynth unsupported" ); } u32 total_size = clip->size + sizeof(struct synth_bird); @@ -1172,7 +1172,7 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) total_size = vg_align8( total_size ); if( total_size > AUDIO_DECODE_SIZE ) - vg_fatal_exit_loop( "Bird coding too long\n" ); + vg_fatal_error( "Bird coding too long\n" ); struct synth_bird *bird = vg_linear_alloc( lin_alloc, total_size ); memcpy( &bird->settings, clip->data, clip->size ); @@ -1184,7 +1184,7 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) } else{ if( !clip->path ){ - vg_fatal_exit_loop( "No path specified, embeded mono unsupported" ); + vg_fatal_error( "No path specified, embeded mono unsupported" ); } vg_linear_clear( vg_mem.scratch ); @@ -1204,7 +1204,7 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) if( !decoder ){ vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", clip->path, err ); - vg_fatal_exit_loop( "Vorbis decode error" ); + vg_fatal_error( "Vorbis decode error" ); } /* only mono is supported in uncompressed */ @@ -1220,7 +1220,7 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) decoder, clip->data, length_samples ); if( read_samples != length_samples ) - vg_fatal_exit_loop( "Decode error" ); + vg_fatal_error( "Decode error" ); float mb = (float)(data_size) / (1024.0f*1024.0f); vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb, @@ -1240,7 +1240,7 @@ VG_STATIC void audio_require_clip_loaded( audio_clip *clip ) return; audio_unlock(); - vg_fatal_exit_loop( "Must load audio clip before playing! \n" ); + vg_fatal_error( "Must load audio clip before playing! \n" ); } /* diff --git a/vg_audio_dsp.h b/vg_audio_dsp.h index 56d3b20..e6a953e 100644 --- a/vg_audio_dsp.h +++ b/vg_audio_dsp.h @@ -24,7 +24,7 @@ static float *dsp_allocate( u32 samples ) samples = vg_align4( samples ); if( vg_dsp.allocations + samples > (1024*1024)/4 ) - vg_fatal_exit_loop( "too much dsp" ); + vg_fatal_error( "too much dsp" ); float *buf = &vg_dsp.buffer[ vg_dsp.allocations ]; vg_dsp.allocations += samples; @@ -139,23 +139,25 @@ static struct dsp_delay __echos[8]; static struct dsp_lpf __echos_lpf[8]; static struct dsp_schroeder __diffusion_chain[8]; -static void vg_dsp_init( void ) +static void async_vg_dsp_alloc_texture( void *payload, u32 size ) { - vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 ); - vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 ); - - vg_acquire_thread_sync(); glGenTextures( 1, &vg_dsp.view_texture ); glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, vg_dsp.view_texture_buffer ); - vg_tex2d_nearest(); - vg_release_thread_sync(); - + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); +} - /* temporary global design */ +static void vg_dsp_init( void ) +{ + vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 ); + vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 ); + vg_async_item *call = vg_async_alloc(0); + vg_async_dispatch( call, async_vg_dsp_alloc_texture ); + /* temporary global design */ dsp_init_lpf( &__lpf_mud_free, 125.0f ); float sizes[] = diff --git a/vg_console.h b/vg_console.h index 9f2cba4..d5da847 100644 --- a/vg_console.h +++ b/vg_console.h @@ -109,7 +109,7 @@ void vg_console_reg_var( const char *alias, void *ptr, enum vg_var_dtype type, u32 flags ) { if( vg_console.var_count > vg_list_size(vg_console.vars) ) - vg_fatal_exit_loop( "Too many vars registered" ); + vg_fatal_error( "Too many vars registered" ); vg_var *var = &vg_console.vars[ vg_console.var_count ++ ]; var->name = alias; @@ -126,7 +126,7 @@ void vg_console_reg_cmd( const char *alias, void (*poll_suggest)(int argc, const char *argv[]) ) { if( vg_console.function_count > vg_list_size(vg_console.functions) ) - vg_fatal_exit_loop( "Too many functions registered" ); + vg_fatal_error( "Too many functions registered" ); vg_cmd *cmd = &vg_console.functions[ vg_console.function_count ++ ]; @@ -242,7 +242,7 @@ VG_STATIC int _vg_console_list( int argc, char const *argv[] ) int _test_break( int argc, const char *argv[] ) { - vg_fatal_exit_loop( "Test crash from main, after loading (console)" ); + vg_fatal_error( "Test crash from main, after loading (console)" ); return 0; } diff --git a/vg_input.h b/vg_input.h index 8a75b70..778d6f0 100644 --- a/vg_input.h +++ b/vg_input.h @@ -565,10 +565,8 @@ VG_STATIC int vg_input_button_down( struct input_binding *bind ) return 0; } -VG_STATIC void vg_input_init(void) +VG_STATIC void async_vg_input_init( void *payload, u32 size ) { - vg_acquire_thread_sync(); - vg_console_reg_cmd( "bind", vg_rebind_input_cmd, vg_rebind_input_cmd_poll ); VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT ); @@ -587,8 +585,12 @@ VG_STATIC void vg_input_init(void) vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERLEFT ] = -1.0f; vg_input.controller_axises[ SDL_CONTROLLER_AXIS_TRIGGERRIGHT ] = -1.0f; +} - vg_release_thread_sync(); +VG_STATIC void vg_input_init(void) +{ + vg_async_item *call = vg_async_alloc(0); + vg_async_dispatch( call, async_vg_input_init ); } VG_STATIC void vg_input_free(void) diff --git a/vg_io.h b/vg_io.h index a97d20a..779b872 100644 --- a/vg_io.h +++ b/vg_io.h @@ -54,11 +54,11 @@ VG_STATIC void *vg_file_read( void *lin_alloc, const char *path, u32 *size ) else{ if( ferror( f ) ){ fclose(f); - vg_fatal_exit_loop( "read error" ); + vg_fatal_error( "read error" ); } else{ fclose(f); - vg_fatal_exit_loop( "unknown error codition" ); + vg_fatal_error( "unknown error codition" ); } } } diff --git a/vg_lines.h b/vg_lines.h index a16f502..84e9632 100644 --- a/vg_lines.h +++ b/vg_lines.h @@ -68,57 +68,58 @@ struct{ } static vg_lines; +VG_STATIC void async_vg_lines_init( void *payload, u32 payload_size ) +{ + glGenVertexArrays( 1, &vg_lines.vao ); + glGenBuffers( 1, &vg_lines.vbo ); + glBindVertexArray( vg_lines.vao ); + glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo ); + + u32 size = 50000 * sizeof( struct vg_lines_vert ); + + vg_lines.vertex_buffer = + vg_create_linear_allocator(vg_mem.rtmemory, size, VG_MEMORY_REALTIME); + + glBufferData( GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW ); + glBindVertexArray( vg_lines.vao ); + VG_CHECK_GL_ERR(); + + /* Pointers */ + glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + sizeof( struct vg_lines_vert ), + (void *)0 + ); + glEnableVertexAttribArray( 0 ); + + glVertexAttribPointer( + 1, + 4, + GL_UNSIGNED_BYTE, + GL_TRUE, + sizeof( struct vg_lines_vert ), + (void*)(offsetof( struct vg_lines_vert, colour )) + ); + glEnableVertexAttribArray( 1 ); + + VG_CHECK_GL_ERR(); + vg_success( "done\n" ); + + vg_lines.allow_input = 1; +} + VG_STATIC void vg_lines_init(void) { - vg_info( "vg_lines_init\n" ); + vg_async_item *call = vg_async_alloc(0); + vg_async_dispatch( call, async_vg_lines_init ); vg_console_reg_var( "vg_lines", &vg_lines.draw, k_var_dtype_i32, VG_VAR_CHEAT ); vg_shader_register( &_shader_lines ); - vg_acquire_thread_sync(); - { - glGenVertexArrays( 1, &vg_lines.vao ); - glGenBuffers( 1, &vg_lines.vbo ); - glBindVertexArray( vg_lines.vao ); - glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo ); - - u32 size = 50000 * sizeof( struct vg_lines_vert ); - - vg_lines.vertex_buffer = - vg_create_linear_allocator(vg_mem.rtmemory, size, VG_MEMORY_REALTIME); - - glBufferData( GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW ); - glBindVertexArray( vg_lines.vao ); - VG_CHECK_GL_ERR(); - - /* Pointers */ - glVertexAttribPointer( - 0, - 3, - GL_FLOAT, - GL_FALSE, - sizeof( struct vg_lines_vert ), - (void *)0 - ); - glEnableVertexAttribArray( 0 ); - - glVertexAttribPointer( - 1, - 4, - GL_UNSIGNED_BYTE, - GL_TRUE, - sizeof( struct vg_lines_vert ), - (void*)(offsetof( struct vg_lines_vert, colour )) - ); - glEnableVertexAttribArray( 1 ); - - VG_CHECK_GL_ERR(); - vg_success( "done\n" ); - } - - vg_release_thread_sync(); - vg_lines.allow_input = 1; } VG_STATIC void vg_lines_drawall( void ) diff --git a/vg_loader.h b/vg_loader.h index ffdd87a..f62763d 100644 --- a/vg_loader.h +++ b/vg_loader.h @@ -145,7 +145,7 @@ VG_STATIC void _vg_loader_init(void) VG_CHECK_GL_ERR(); if( !vg_shader_compile( &_shader_loader ) ) - vg_fatal_exit_loop( "failed to compile shader" ); + vg_fatal_error( "failed to compile shader" ); } VG_STATIC void _vg_loader_free(void) @@ -234,28 +234,22 @@ VG_STATIC void vg_load_full(void); VG_STATIC int _vg_loader_thread(void *pfn) { - SDL_AtomicLock( &vg.sl_context ); - vg.thread_id_loader = SDL_GetThreadID(NULL); - VG_SYNC_LOG( "[%d] Loader thread begins\n" ); - SDL_AtomicUnlock( &vg.sl_context ); + if( setjmp( vg.env_loader_exit ) ) + return 0; /* Run client loader */ + vg_info( "Starting client loader thread @%p\n", pfn ); void (*call_func)(void) = pfn; call_func(); SDL_SemPost( vg.sem_loader ); vg.thread_id_loader = 0; - vg_acquire_thread_sync(); - vg.is_loaded = 1; - vg_release_thread_sync(); - return 0; } VG_STATIC void vg_loader_start( void(*pfn)(void) ) { - vg.is_loaded = 0; SDL_SemWait( vg.sem_loader ); SDL_CreateThread( _vg_loader_thread, "Loader thread", pfn ); } @@ -268,18 +262,17 @@ VG_STATIC void vg_loader_step( void( *fn_load )(void), void( *fn_free )(void) ) if( fn_load ) fn_load(); - if( fn_free ) - { + if( fn_free ){ struct loader_free_step step; step.fn_free = fn_free; if( vg_loader.step_count == vg_list_size(vg_loader.step_buffer) ) - vg_fatal_exit_loop( "Too many free steps" ); + vg_fatal_error( "Too many free steps" ); vg_loader.step_buffer[ vg_loader.step_count ++ ] = step; } - _vg_ensure_engine_running(); + /* TODO: There was a quit checker here, re-add this? */ } #endif /* VG_LOADER_H */ diff --git a/vg_mem.h b/vg_mem.h index 49d9e37..598bc83 100644 --- a/vg_mem.h +++ b/vg_mem.h @@ -88,7 +88,7 @@ struct vg_linear_allocator }; #pragma pack(pop) -VG_STATIC void vg_fatal_exit_loop( const char *error ); +VG_STATIC void vg_fatal_error( const char *fmt, ... ); VG_STATIC void vg_error(const char *fmt, ...); VG_STATIC void vg_info(const char *fmt, ...); @@ -127,20 +127,19 @@ VG_STATIC void *vg_linear_alloc( void *buffer, u32 size ) #else if( ((u64)buffer) % 8 ){ #endif - vg_error( "buffer: %p\n", buffer ); - vg_fatal_exit_loop( "unaligned buffer" ); + vg_fatal_error( "unaligned buffer (%p)", buffer ); } vg_linear_allocator *alloc = vg_linear_header( buffer ); if( (alloc->cur + size) > alloc->size ){ - vg_error( "%u + %u > %u\n", alloc->cur, size, alloc->size ); - vg_fatal_exit_loop( "linear allocator overflow" ); + vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n", + alloc->cur, size, alloc->size ); } if( alloc->flags & VG_MEMORY_SYSTEM ) if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS ) - vg_fatal_exit_loop( "Max linear allocations reached" ); + vg_fatal_error( "Max linear allocations reached" ); void *data; @@ -157,7 +156,7 @@ VG_STATIC void *vg_linear_alloc( void *buffer, u32 size ) u8 *bytes = data; for( u32 i=0; iallocation_count ++; @@ -170,7 +169,7 @@ VG_STATIC void *vg_linear_alloc( void *buffer, u32 size ) #else if( ((u64)data) % 8 ){ #endif - vg_fatal_exit_loop( "unaligned" ); + vg_fatal_error( "unaligned" ); } return data; @@ -189,10 +188,10 @@ VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize ) } if( alloc->last_alloc != data ) - vg_fatal_exit_loop( "This block has been fixed!" ); + vg_fatal_error( "This block has been fixed!" ); if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size ) - vg_fatal_exit_loop( "Cannot resize, overflow" ); + vg_fatal_error( "Cannot resize, overflow" ); alloc->cur -= alloc->last_alloc_size; alloc->cur += newsize; @@ -201,7 +200,7 @@ VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize ) if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){ data = realloc( data, newsize ); if( !data ) - vg_fatal_exit_loop( "realloc failed" ); + vg_fatal_error( "realloc failed" ); alloc->alloc_table[ alloc->allocation_count-1 ].data = data; alloc->last_alloc = data; @@ -218,12 +217,12 @@ VG_STATIC void vg_linear_del( void *buffer, void *data ) vg_linear_allocator *alloc = vg_linear_header( buffer ); if( alloc->last_alloc != data ) - vg_fatal_exit_loop( "This block has been fixed!" ); + vg_fatal_error( "This block has been fixed!" ); if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){ vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1]; if( meta->type == k_allocation_type_linear ) - vg_fatal_exit_loop( "Cannot free a linear allocator in this conext" ); + vg_fatal_error( "Cannot free a linear allocator in this conext" ); free( data ); } @@ -244,7 +243,7 @@ VG_STATIC void *vg_linear_extend( void *buffer, void *data, u32 extra ) vg_linear_allocator *alloc = vg_linear_header( buffer ); if( alloc->last_alloc != data ) - vg_fatal_exit_loop( "This block has been fixed!" ); + vg_fatal_error( "This block has been fixed!" ); u32 new_size = alloc->last_alloc_size + extra; return vg_linear_resize( buffer, data, new_size ); @@ -314,13 +313,13 @@ VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size, vg_linear_allocator *alloc = vg_linear_header( lin_alloc ); if( alloc->cur + block_size > alloc->size ) - vg_fatal_exit_loop( "Out of memory" ); + vg_fatal_error( "Out of memory" ); if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS ) - vg_fatal_exit_loop( "Max allocations in linear allocator" ); + vg_fatal_error( "Max allocations in linear allocator" ); if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) ) - vg_fatal_exit_loop( "Cannot declare realtime allocator inside systems" + vg_fatal_error( "Cannot declare realtime allocator inside systems" " allocator" ); if( vg_mem.use_libc_malloc ){ diff --git a/vg_shader.h b/vg_shader.h index 50e8bd0..86b6ba6 100644 --- a/vg_shader.h +++ b/vg_shader.h @@ -184,7 +184,7 @@ VG_STATIC void vg_shaders_compile(void) vg_shader *shader = vg_shaders.shaders[i]; if( !vg_shader_compile( shader ) ) - vg_fatal_exit_loop( "Failed to compile shader" ); + vg_fatal_error( "Failed to compile shader" ); } } @@ -203,7 +203,7 @@ VG_STATIC int vg_shaders_live_recompile(int argc, const char *argv[]) VG_STATIC void vg_shader_register( struct vg_shader *shader ) { if( vg_shaders.count == vg_list_size(vg_shaders.shaders) ) - vg_fatal_exit_loop( "Too many shaders" ); + vg_fatal_error( "Too many shaders" ); shader->compiled = 0; shader->id = 0; /* TODO: make this an error shader */ diff --git a/vg_tex.h b/vg_tex.h index f570b11..e857aa8 100644 --- a/vg_tex.h +++ b/vg_tex.h @@ -6,197 +6,278 @@ #include "vg/vg.h" #include "vg/vg_log.h" -#define VG_TEXTURE_NO_MIP 0x1 -#define VG_TEXTURE_REPEAT 0x2 -#define VG_TEXTURE_CLAMP 0x4 -#define VG_TEXTURE_NEAREST 0x8 -#define VG_TEXTURE_ALLOCATED_INTERNAL 0x10 - -VG_STATIC void *vg_qoi_malloc( size_t size ) -{ - return vg_linear_alloc( vg_mem.scratch, size ); -} - -VG_STATIC void vg_qoi_free( void *ptr ) -{ - -} - -#define QOI_IMPLEMENTATION -#define QOI_NO_STDIO -#define QOI_MALLOC(sz) vg_qoi_malloc( sz ) -#define QOI_FREE(p) vg_qoi_free( p ) - -#include "submodules/qoi/qoi.h" - -struct vg_tex2d -{ - const char *path; - u32 flags; - GLuint name; -}; - struct vg_sprite { v4f uv_xywh; }; -VG_STATIC void vg_tex2d_bind( vg_tex2d *tex, u32 id ) -{ - if( !(tex->flags & VG_TEXTURE_ALLOCATED_INTERNAL) ) - { - vg_error( "Tried to use '%s' while unloaded!\n", tex->path ); - return; - } +#define VG_TEX2D_LINEAR 0x1 +#define VG_TEX2D_NEAREST 0x2 +#define VG_TEX2D_REPEAT 0x4 +#define VG_TEX2D_CLAMP 0x8 +#define VG_TEX2D_NOMIP 0x10 - glActiveTexture( GL_TEXTURE0 + id ); - glBindTexture( GL_TEXTURE_2D, tex->name ); -} +static u8 const_vg_tex2d_err[] = +{ + 0xff, 0xff, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff +}; -static inline void vg_tex2d_mipmap(void) -{ - glGenerateMipmap( GL_TEXTURE_2D ); +#define QOI_SRGB 0 +#define QOI_LINEAR 1 + +typedef struct { + unsigned int width; + unsigned int height; + unsigned char channels; + unsigned char colorspace; +} qoi_desc; + +#ifndef QOI_ZEROARR + #define QOI_ZEROARR(a) memset((a),0,sizeof(a)) +#endif + +#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ +#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ +#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ +#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ +#define QOI_OP_RGB 0xfe /* 11111110 */ +#define QOI_OP_RGBA 0xff /* 11111111 */ + +#define QOI_MASK_2 0xc0 /* 11000000 */ + +#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) +#define QOI_MAGIC \ + (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ + ((unsigned int)'i') << 8 | ((unsigned int)'f')) +#define QOI_HEADER_SIZE 14 + +/* 2GB is the max file size that this implementation can safely handle. We guard +against anything larger than that, assuming the worst case with 5 bytes per +pixel, rounded down to a nice clean value. 400 million pixels ought to be +enough for anybody. */ +#define QOI_PIXELS_MAX ((unsigned int)400000000) + +typedef union { + struct { unsigned char r, g, b, a; } rgba; + unsigned int v; +} qoi_rgba_t; + +static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1}; +static u32 qoi_read_32( const u8 *bytes, int *p ) { + u32 a = bytes[(*p)++]; + u32 b = bytes[(*p)++]; + u32 c = bytes[(*p)++]; + u32 d = bytes[(*p)++]; + return a << 24 | b << 16 | c << 8 | d; } -static inline void vg_tex2d_linear(void) -{ - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); -} +struct texture_load_info{ + GLuint *dest; + u32 width, height, flags; + u8 *rgba; +}; -static inline void vg_tex2d_nearest(void) +VG_STATIC void async_vg_tex2d_upload( void *payload, u32 size ) { - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); -} + struct texture_load_info *info = payload; -static inline void vg_tex2d_linear_mipmap(void) -{ - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_LINEAR_MIPMAP_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); -} + glGenTextures( 1, info->dest ); + glBindTexture( GL_TEXTURE_2D, *info->dest ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info->width, info->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba ); -static inline void vg_tex2d_repeat(void) -{ - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); -} + if( !(info->flags & VG_TEX2D_NOMIP) ){ + glGenerateMipmap( GL_TEXTURE_2D ); + } -static inline void vg_tex2d_clamp(void) -{ - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); -} + if( info->flags & VG_TEX2D_LINEAR ){ + if( info->flags & VG_TEX2D_NOMIP ){ + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else{ + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR ); + } + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } -VG_STATIC GLuint vg_tex2d_new(void) -{ - GLuint texture_name; - glGenTextures( 1, &texture_name ); - glBindTexture( GL_TEXTURE_2D, texture_name ); + if( info->flags & VG_TEX2D_NEAREST ){ + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } - return texture_name; -} + if( info->flags & VG_TEX2D_CLAMP ){ + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } -VG_STATIC void vg_tex2d_set_error(void) -{ - u32 tex_err[4] = - { - 0xffff00ff, - 0xff000000, - 0xff000000, - 0xffff00ff - }; - - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, - 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_err ); + if( info->flags & VG_TEX2D_REPEAT ){ + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + } } -VG_STATIC void vg_tex2d_qoi( void *mem, u32 size, const char *name ) +VG_STATIC void vg_tex2d_replace_with_error( GLuint *dest ) { - qoi_desc info; - u8 *tex_buffer = qoi_decode( mem, size, &info, 4 ); + u32 hdr_size = vg_align8(sizeof(struct texture_load_info)); - if( tex_buffer ) - { - vg_info( "Texture decoded: [%u %u] %s\n", - info.width, info.height, name ); - - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_buffer ); + vg_async_item *call = vg_async_alloc( hdr_size ); + struct texture_load_info *info = call->payload; + + info->dest = dest; + info->flags = VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP; + info->width = 2; + info->height = 2; + info->rgba = const_vg_tex2d_err; - QOI_FREE(tex_buffer); - } - else - { - vg_error( "File size: %u\n", size ); - vg_tex2d_set_error(); - } + vg_async_dispatch( call, async_vg_tex2d_upload ); } -/* - * TODO: This blocks while we read from file - */ -VG_STATIC GLuint vg_tex2d_rgba( const char *path ) +VG_STATIC +void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, + u32 flags, GLuint *dest ) { - GLuint texture_name = vg_tex2d_new(); + u32 header_magic; + qoi_rgba_t index[64]; + qoi_rgba_t px; + int px_len, chunks_len, px_pos; + int p = 0, run = 0; + + u32 channels = 4; /* TODO */ + + qoi_desc desc; + + if ( + bytes == NULL || + (channels != 0 && channels != 3 && channels != 4) || + size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding) + ) { + vg_error( "Error while decoding qoi file: illegal parameters\n" ); + vg_tex2d_replace_with_error( dest ); + return; + } - vg_linear_clear( vg_mem.scratch ); - u32 size; - void *file = vg_file_read( vg_mem.scratch, path, &size ); + header_magic = qoi_read_32(bytes, &p); + desc.width = qoi_read_32(bytes, &p); + desc.height = qoi_read_32(bytes, &p); + desc.channels = bytes[p++]; + desc.colorspace = bytes[p++]; + + if ( + desc.width == 0 || desc.height == 0 || + desc.channels < 3 || desc.channels > 4 || + desc.colorspace > 1 || + header_magic != QOI_MAGIC || + desc.height >= QOI_PIXELS_MAX / desc.width + ) { + vg_error( "Error while decoding qoi file: invalid file\n" ); + vg_tex2d_replace_with_error( dest ); + return; + } - if( file ) - { - vg_tex2d_qoi( file, size, path ); - } - else - { - vg_error( "Loading texture failed (%s)\n", path ); - vg_tex2d_set_error(); - } + if (channels == 0) { + channels = desc.channels; + } - return texture_name; -} + px_len = desc.width * desc.height * channels; -VG_STATIC void vg_tex2d_init( vg_tex2d *textures[], int num ) -{ - for( int i=0; iname = vg_tex2d_rgba( tex->path ); - if( !(tex->flags & VG_TEXTURE_NO_MIP) ) - vg_tex2d_mipmap(); - - if( tex->flags & VG_TEXTURE_NEAREST ) - { - if( tex->flags & VG_TEXTURE_NO_MIP ) - vg_error( "Invalid texture settings\n" ); - else - vg_tex2d_nearest(); + /* allocate async call + * -------------------------- + */ + 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; + + info->dest = dest; + info->flags = flags; + info->width = desc.width; + info->height = desc.height; + info->rgba = ((u8*)call->payload) + hdr_size; + + /* + * Decode + * ---------------------------- + */ + + u8 *pixels = info->rgba; + + QOI_ZEROARR(index); + px.rgba.r = 0; + px.rgba.g = 0; + px.rgba.b = 0; + px.rgba.a = 255; + + chunks_len = size - (int)sizeof(qoi_padding); + for (px_pos = 0; px_pos < px_len; px_pos += channels) { + if (run > 0) { + run--; } - else - { - if( tex->flags & VG_TEXTURE_NO_MIP ) - vg_tex2d_linear(); - else - vg_tex2d_linear_mipmap(); + else if (p < chunks_len) { + int b1 = bytes[p++]; + + if (b1 == QOI_OP_RGB) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + } + else if (b1 == QOI_OP_RGBA) { + px.rgba.r = bytes[p++]; + px.rgba.g = bytes[p++]; + px.rgba.b = bytes[p++]; + px.rgba.a = bytes[p++]; + } + else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { + px = index[b1]; + } + else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { + px.rgba.r += ((b1 >> 4) & 0x03) - 2; + px.rgba.g += ((b1 >> 2) & 0x03) - 2; + px.rgba.b += ( b1 & 0x03) - 2; + } + else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { + int b2 = bytes[p++]; + int vg = (b1 & 0x3f) - 32; + px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); + px.rgba.g += vg; + px.rgba.b += vg - 8 + (b2 & 0x0f); + } + else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { + run = (b1 & 0x3f); + } + + index[QOI_COLOR_HASH(px) % 64] = px; } - - if( tex->flags & VG_TEXTURE_CLAMP ) - vg_tex2d_clamp(); - else - vg_tex2d_repeat(); - tex->flags |= VG_TEXTURE_ALLOCATED_INTERNAL; + pixels[px_pos + 0] = px.rgba.r; + pixels[px_pos + 1] = px.rgba.g; + pixels[px_pos + 2] = px.rgba.b; + + if (channels == 4) { + pixels[px_pos + 3] = px.rgba.a; + } } + + /* + * Complete the call + * -------------------------- + */ + + vg_async_dispatch( call, async_vg_tex2d_upload ); } -VG_STATIC void vg_tex2d_free( vg_tex2d *textures[], int num ) +VG_STATIC +void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest ) { - for( int i = 0; i < num; i ++ ) - { - glDeleteTextures( 1, &textures[i]->name ); - } + vg_linear_clear( vg_mem.scratch ); + + u32 size; + const void *data = vg_file_read( vg_mem.scratch, path, &size ); + vg_tex2d_load_qoi_async( data, size, flags, dest ); } #endif /* VG_TEX_H */ diff --git a/vg_ui.h b/vg_ui.h index 1e120e2..3c16711 100644 --- a/vg_ui.h +++ b/vg_ui.h @@ -232,7 +232,7 @@ VG_STATIC void _vg_ui_init(void) { if( !vg_shader_compile( &_shader_ui ) || !vg_shader_compile( &_shader_ui_image ) ) - vg_fatal_exit_loop( "Failed to compile ui shader" ); + vg_fatal_error( "Failed to compile ui shader" ); /* * Vertex buffer @@ -324,8 +324,10 @@ VG_STATIC void _vg_ui_init(void) GL_RED, GL_UNSIGNED_BYTE, image ); VG_CHECK_GL_ERR(); - vg_tex2d_clamp(); - vg_tex2d_nearest(); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } static struct ui_vert *ui_fill_rect_uv( ui_rect rect, u32 colour, ui_px uv[4] );