switch to async system
authorhgn <hgodden00@gmail.com>
Mon, 24 Apr 2023 11:47:51 +0000 (12:47 +0100)
committerhgn <hgodden00@gmail.com>
Mon, 24 Apr 2023 11:47:51 +0000 (12:47 +0100)
14 files changed:
submodules/SDL
vg.h
vg_async.h
vg_audio.h
vg_audio_dsp.h
vg_console.h
vg_input.h
vg_io.h
vg_lines.h
vg_loader.h
vg_mem.h
vg_shader.h
vg_tex.h
vg_ui.h

index 06492c598158cf825a18aececaf7511d7fd04f48..eef4d3c86a653f91b7221c80809ba8ab56f94cf1 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 06492c598158cf825a18aececaf7511d7fd04f48
+Subproject commit eef4d3c86a653f91b7221c80809ba8ab56f94cf1
diff --git a/vg.h b/vg.h
index 7ddd04ef839871dc19b845c60a23d7a02043ec78..9ad04b1f659ca600fc9529fcd3399ef9f9dfea0b 100644 (file)
--- a/vg.h
+++ b/vg.h
 |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 <setjmp.h>
 
   //#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);
 }
 
index 366f448108221c18b0a910f1d8d66a84d31c117d..190b5a8271552ae1dae6ff71646a45febca94b82 100644 (file)
@@ -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 */
index cf6651ed6930e4777c3dff5d18aeef3affe955b6..34bc9ee4bac5fdbb558a8012fc5385b6f1042620 100644 (file)
@@ -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" );
 }
 
 /* 
index 56d3b20c655cb8050999f87287531bf76cf59a41..e6a953ec5cae6e7c8acd6f356388d27b3e46aac3 100644 (file)
@@ -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[] = 
index 9f2cba4eb647f03e2cdc867e7b2f4e9acefaa863..d5da84729b2f5e770baf1863c3b8406aaf4f73ef 100644 (file)
@@ -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;
 }
 
index 8a75b70a37988e77741e0e98b4869e2fbd4f50c7..778d6f09c2ec8455e35c9fa1aad5fe775fce8a53 100644 (file)
@@ -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 a97d20a0c6d4c0fcf6a6e9fb50c04460fa07a17a..779b8726c09db9e81d45063422518bde1f3bbe92 100644 (file)
--- 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" );
                }
             }
          }
index a16f5020481a35a7ce3b76048859c144b89d3238..84e9632d9a90f863839e2f9c5a6c9359a0d94dd9 100644 (file)
@@ -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 )
index ffdd87ad3c9d9803ee9a131f9b84513c25b033f7..f62763d5ca63445115a206cb3fc5d296efefbac0 100644 (file)
@@ -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 */
index 49d9e37e86b8af8d515a64a164d64873e6d6f59f..598bc83c352f3c7e382a0835cad459b0cbb2a113 100644 (file)
--- 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; i<size; i++ ){
-      bytes[i] = 0xae;
+      bytes[i] = 0xfe;
    }
 
    alloc->allocation_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 ){
index 50e8bd0e291589a17d32b17fe1c11f0038e9234e..86b6ba6d928d100bf4844bf70f8f352c4c2cee2c 100644 (file)
@@ -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 */
index f570b11684fe367af76f8819dec2501050b5e307..e857aa85858c436558aa69d9887808661b9db6c9 100644 (file)
--- a/vg_tex.h
+++ b/vg_tex.h
 #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; i<num; i ++ )
-       {
-               vg_tex2d *tex = textures[i];
-               tex->name = 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 1e120e28aad5e9e67f3b1796401a19181d9824a3..3c16711619139ef0fb31a3886bf9f06829b16d3e 100644 (file)
--- 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] );