tune reverb
[vg.git] / vg.h
diff --git a/vg.h b/vg.h
index a7b994d3348fb010010163dced0a395c9b799b31..40767a2a0746cf20fce277d0eb8d658f7c936eb0 100644 (file)
--- a/vg.h
+++ b/vg.h
@@ -2,11 +2,11 @@
 
 /*
  
- .-.                         VG Event loop
+ .-.                           VG Event loop
 | 0 |
-|   |  .-----------.
-|API| |  vg_enter   |
-|   |  '-----------'
+|   |  .---------------------------------------------------------.
+|API| | vg_enter( int argc, char *argv[], const char *window_name |
+|   |  '---------------------------------------------------------'
 |   |        |
 |   |        v
 |IMP|  vg_launch_opt(void) <--.
 |IMP| |        |.------------- vg_start(void) ---------------'
 |   | |        |
 |   | |        v
-|IMP| |   vg_update( int loaded )
+|IMP| |   vg_update(void)
 |   | |        |                                     
 |   | |  .-----+. 
 |   | | |        |
 |   | | |        v
-|IMP| |  '- vg_update_fixed( int loaded )
+|IMP| |  '- vg_update_fixed(void)
 |   | |          |
 |   | |       .-'
 |   | |      |
 |IMP| |   vg_ui(void)
 |   | |      |
 |   |  '----'
+'___'
+
+ .-.
+| ? |
+|   |  .-------------------------------------.
+|API| | vg_fatal_exit_loop( const char *err ) |
+|   |  '-------------------------------------'
+|   |         |
+|   |  .------+.
+|   | |         |
+|   | |         v
+|IMP|  '- vg_framebuffer_resize(void)
 '___'
 
 */
@@ -65,9 +77,9 @@ VG_STATIC void vg_launch_opt(void);
 VG_STATIC void vg_start(void);
 
 VG_STATIC void vg_framebuffer_resize(int w, int h);
-VG_STATIC void vg_update(int loaded);
-VG_STATIC void vg_update_fixed(int loaded);
-VG_STATIC void vg_update_post(int loaded);
+VG_STATIC void vg_update(void);
+VG_STATIC void vg_update_fixed(void);
+VG_STATIC void vg_update_post(void);
 
 VG_STATIC void vg_render(void);
 VG_STATIC void vg_ui(void);
@@ -116,7 +128,7 @@ struct vg
    SDL_threadID  thread_id_main,
                  thread_id_loader,
                  thread_id_with_opengl_context;
-   int           context_ownership_refcount;
+   int           context_ownership_depth;
 
    int exec_context;
 
@@ -174,102 +186,87 @@ struct vg
 }
 VG_STATIC vg = { .time_rate = 1.0 };
 
-struct vg_thread_info
+enum vg_thread_purpose
 {
-   enum vg_thread_purpose
-   {
-      k_thread_purpose_nothing,
-      k_thread_purpose_main,
-      k_thread_purpose_loader
-   }
-   purpose;
-
-   int gl_context_level;
+   k_thread_purpose_nothing,
+   k_thread_purpose_main,
+   k_thread_purpose_loader
 };
 
 VG_STATIC void vg_fatal_exit_loop( const char *error );
-VG_STATIC void vg_required( void *ptr, const char *path )
-{
-   if( !ptr )
-   {
-      vg_fatal_exit_loop( path );
-   }
-}
 
-VG_STATIC void vg_ensure_engine_running(void)
+/*
+ * Checks if the engine is running
+ */
+VG_STATIC void _vg_ensure_engine_running(void)
 {
-   VG_SYNC_LOG( "[%d] Checks if engine is running\n" );
-
    /* Check if the engine is no longer running */
    SDL_AtomicLock( &vg.sl_context );
+   enum engine_status status = vg.engine_status;
+   SDL_AtomicUnlock( &vg.sl_context );
 
-   if( vg.engine_status != k_engine_status_running )
+   if( status != k_engine_status_running )
    {
-      VG_SYNC_LOG( "[%d] Engine is no longer running\n");
-
-      /* Safe to disregard loader thread from this point on, elswhere */
-      if( vg.thread_id_loader == SDL_GetThreadID(NULL) )
+      while(1) 
       {
-         SDL_SemPost( vg.sem_loader );
+         VG_SYNC_LOG( "[%d] No longer running...\n");
+         SDL_Delay(1000);
       }
-
-      SDL_AtomicUnlock( &vg.sl_context );
-
-      VG_SYNC_LOG( "[%d] is just going to hang around and wait to die\n");
-      while(1) SDL_Delay(1000);
    }
-
-   SDL_AtomicUnlock( &vg.sl_context );
 }
 
-/*
- * 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)
+VG_STATIC enum vg_thread_purpose vg_thread_purpose(void)
 {
-   VG_SYNC_LOG( "[%d] Starts acquiring sync\n" );
-
-   /* We dont want to do anything if this is the main thread */
    SDL_AtomicLock( &vg.sl_context );
+
    if( vg.thread_id_main == SDL_GetThreadID(NULL) )
    {
       SDL_AtomicUnlock( &vg.sl_context );
-      return;
+      return k_thread_purpose_main;
    }
-   SDL_AtomicUnlock( &vg.sl_context );
-
-   vg_ensure_engine_running();
-
-   /* Check if thread already has the context */
-   SDL_AtomicLock( &vg.sl_context );
-   if( vg.thread_id_with_opengl_context == SDL_GetThreadID(NULL) )
+   else
    {
-      vg.context_ownership_refcount ++;
       SDL_AtomicUnlock( &vg.sl_context );
-
-      VG_SYNC_LOG( "[%d] We already have sync here\n" );
-      return;
+      return k_thread_purpose_loader;
    }
+}
 
-   VG_SYNC_LOG( "[%d] Signal to sync.\n" );
-   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 );
-   SDL_GL_MakeCurrent( vg.window, vg.gl_context );
+/*
+ * 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 */
 
-   VG_SYNC_LOG( "[%d] Allow exec passed\n" );
+   if( vg_thread_purpose() == k_thread_purpose_loader )
+   {
+      VG_SYNC_LOG( "[%d] vg_acquire_thread_sync()\n" );
+      _vg_ensure_engine_running();
 
-   /* context now valid to work in while we hold up main thread */
-   SDL_AtomicLock( &vg.sl_context );
-   vg.context_ownership_refcount ++;
-   SDL_AtomicUnlock( &vg.sl_context );
+      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();
 
-   VG_SYNC_LOG( "[%d] Context acquired.\n" );
+         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 );
+      }
+   }
 }
 
 /*
@@ -278,37 +275,32 @@ VG_STATIC void vg_acquire_thread_sync(void)
  */
 VG_STATIC void vg_release_thread_sync(void)
 {
-   VG_SYNC_LOG( "[%d] Releases sync\n" );
-
-   SDL_AtomicLock( &vg.sl_context );
-   if( vg.thread_id_main == SDL_GetThreadID(NULL) )
+   if( vg_thread_purpose() == k_thread_purpose_loader )
    {
-      SDL_AtomicUnlock( &vg.sl_context );
-      return;
-   }
+      VG_SYNC_LOG( "[%d] vg_release_thread_sync()\n" );
 
-   /* signal that we are done */
-   vg.context_ownership_refcount --;
-   if( !vg.context_ownership_refcount )
-   {
-      SDL_AtomicUnlock( &vg.sl_context );
-
-      VG_SYNC_LOG( "[%d] Releasing context.\n" );
+      SDL_AtomicLock( &vg.sl_context );
+      vg.context_ownership_depth --;
 
-      SDL_GL_MakeCurrent( NULL, NULL );
-      SDL_SemPost( vg.sem_exec_finished );
+      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 );
    }
-   else
-      SDL_AtomicUnlock( &vg.sl_context );
 }
 
-VG_STATIC void vg_run_synced_content(void)
+VG_STATIC void _vg_run_synced(void)
 {
    SDL_AtomicLock( &vg.sl_context );
 
    if( vg.exec_context != 0 )
    {
-      VG_SYNC_LOG( "[%d] Allowing content (%d).\n", vg.exec_context );
+      VG_SYNC_LOG( "[%d] _vg_run_synced() (%d).\n", vg.exec_context );
       vg.exec_context = 0;
       SDL_AtomicUnlock( &vg.sl_context );
 
@@ -321,17 +313,20 @@ VG_STATIC void vg_run_synced_content(void)
       SDL_SemWait( vg.sem_exec_finished );
       
       /* check if we killed the engine */
-      vg_ensure_engine_running();
+      _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_STATIC void _vg_opengl_sync_init(void)
 {
    vg.sem_allow_exec = SDL_CreateSemaphore(0);
    vg.sem_exec_finished = SDL_CreateSemaphore(0);
@@ -376,37 +371,31 @@ VG_STATIC void vg_bake_shaders(void)
 {
    vg_acquire_thread_sync();
 
-#if 0
    vg_function_push( (struct vg_cmd)
    {
-      .name = "shaders",
+      .name = "reload_shaders",
       .function = vg_shaders_live_recompile
    });
-#endif
 
    vg_shaders_compile();
    vg_release_thread_sync();
 }
 
-VG_STATIC void vg_load_full(void)
+VG_STATIC void _vg_load_full(void)
 {
    vg_preload();
 
    /* internal */
-   vg_loader_highwater( vg_input_init, vg_input_free, NULL );
-   vg_loader_highwater( vg_lines_init, NULL, NULL );
-   vg_loader_highwater( vg_audio_init, vg_audio_free, NULL );
-   vg_loader_highwater( vg_profiler_init, NULL, NULL );
+   vg_loader_step( vg_input_init, vg_input_free );
+   vg_loader_step( vg_lines_init, NULL );
+   vg_loader_step( vg_audio_init, vg_audio_free );
+   vg_loader_step( vg_profiler_init, NULL );
 
    /* client */
    vg_load();
-
-   vg_acquire_thread_sync();
-   vg.is_loaded = 1;
-   vg_release_thread_sync();
 }
 
-VG_STATIC void vg_process_events(void)
+VG_STATIC void _vg_process_events(void)
 {
    /* Update timers */
    vg.time_real_last = vg.time_real;
@@ -481,12 +470,12 @@ VG_STATIC void vg_process_events(void)
    vg_update_inputs();
 }
 
-VG_STATIC void vg_gameloop_update( int post_start )
+VG_STATIC void _vg_gameloop_update(void)
 {
    vg_profile_begin( &vg_prof_update );
 
    vg.engine_stage = k_engine_stage_update;
-   vg_update( post_start );
+   vg_update();
 
    /* Fixed update loop */
    vg.engine_stage = k_engine_stage_update_fixed;
@@ -496,7 +485,7 @@ VG_STATIC void vg_gameloop_update( int post_start )
    vg_lines.allow_input = 1;
    while( vg.accumulator >= (VG_TIMESTEP_FIXED-0.00125) )
    {
-      vg_update_fixed( post_start );
+      vg_update_fixed();
       vg_lines.allow_input = 0;
 
       vg.accumulator -= VG_TIMESTEP_FIXED;
@@ -511,15 +500,15 @@ VG_STATIC void vg_gameloop_update( int post_start )
    vg_lines.allow_input = 1;
 
    vg.engine_stage = k_engine_stage_update;
-   vg_update_post( post_start );
+   vg_update_post();
    vg_profile_end( &vg_prof_update );
 }
 
-VG_STATIC void vg_gameloop_render( int post_start )
+VG_STATIC void _vg_gameloop_render(void)
 {
    vg_profile_begin( &vg_prof_render );
 
-   if( post_start )
+   if( vg.is_loaded )
    {
       /* render */
       vg.engine_stage = k_engine_stage_rendering;
@@ -558,9 +547,10 @@ VG_STATIC void vg_gameloop_render( int post_start )
             ui_text( (ui_rect){258, 4+24+12,0,0},perf, 1,0);
          }
 
+         /* FIXME */
          audio_debug_ui( vg.pv );
          vg_ui();
-         vg_console_draw();
+         _vg_console_draw();
          
          ui_resolve();
          ui_draw( NULL );
@@ -570,14 +560,14 @@ VG_STATIC void vg_gameloop_render( int post_start )
    vg_profile_end( &vg_prof_render );
 }
 
-VG_STATIC void vg_gameloop(void)
+VG_STATIC void _vg_gameloop(void)
 {
    vg.accumulator = 0.75f * (1.0f/60.0f);
 
    int post_start = 0;
    while(1)
    {
-      vg_process_events();
+      _vg_process_events();
 
       if( vg.window_should_close )
          break;
@@ -596,18 +586,18 @@ VG_STATIC void vg_gameloop(void)
       }
       else
       {
-         vg_loader_render();
+         _vg_loader_render();
       }
 
-      vg_gameloop_update( post_start );
-      vg_gameloop_render( post_start );
+      _vg_gameloop_update();
+      _vg_gameloop_render();
 
       SDL_GL_SwapWindow( vg.window );
-      vg_run_synced_content();
+      _vg_run_synced();
    }
 }
 
-VG_STATIC void vg_process_launch_opts_internal(void)
+VG_STATIC void _vg_process_launch_opts_internal( int argc, char *argv[] )
 {
    char *arg;
    while( vg_argp( argc, argv ) )
@@ -629,7 +619,7 @@ VG_STATIC void vg_process_launch_opts_internal(void)
 
       if(  vg_long_opt( "use-libc-malloc" ) )
       {
-         vg_mem.use_libc_malloc = atoi( arg );
+         vg_mem.use_libc_malloc = 1;
       }
 
       if( vg_long_opt( "high-performance" ) )
@@ -641,73 +631,109 @@ VG_STATIC void vg_process_launch_opts_internal(void)
    }
 }
 
-VG_STATIC void vg_init_window(void)
+VG_STATIC void _vg_init_window( const char *window_name )
 {
-   if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_GAMECONTROLLER) != 0  )
-   {
+   vg_info( "SDL_INIT\n" );
+
+   if( SDL_Init( SDL_INIT_VIDEO ) != 0  ){
       vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
       exit(0);
    }
 
-   SDL_GL_SetSwapInterval( 1 );
+   SDL_InitSubSystem( SDL_INIT_AUDIO );
+   SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER );
+
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 
                            SDL_GL_CONTEXT_PROFILE_CORE );
 
-   SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 1 );
+   SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR,
+                           SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
+   
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
+   SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
 
-   if( vg.samples > 1 )
-   {
-      SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
-      SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, vg.samples );
-   }
-
-   SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR,
-                           SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
-   
    /* 
     * Get monitor information 
     */
+   vg_info( "Getting display count\n" );
    int display_count = 0, display_index = 0, mode_index = 0;
-   if( (display_count = SDL_GetNumVideoDisplays()) < 1 )
-   {
+   if( (display_count = SDL_GetNumVideoDisplays()) < 1 ){
       vg_error( "SDL_GetNumVideoDisplays returned: %i\n", display_count );
       exit(0);
    }
 
+
+#ifdef VG_DEVWINDOW
+   vg.refresh_rate = 60;
+   vg.window_x = 1000;
+   vg.window_y = 800;
+#else
    /* TODO: Allow chosing the modes at startup */
-   SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
-   if( SDL_GetDisplayMode( display_index, mode_index, &mode ) != 0 )
-   {
+   vg_info( "Getting default display mode\n" );
+   SDL_DisplayMode vm_ideal = { 0, 0, 0, 0, 0 };
+   if( SDL_GetDisplayMode( display_index, mode_index, &vm_ideal ) != 0 ){
       vg_error( "SDL_GetDisplayMode failed: %s", SDL_GetError() );
       exit(0);
    }
 
-   vg.refresh_rate = mode.refresh_rate;
+   if( vg.window_x )
+      vm_ideal.w = vg.window_x;
+
+   if( vg.window_y )
+      vm_ideal.h = vg.window_y;
+
+
+   SDL_DisplayMode vm_best;
+   if( !SDL_GetClosestDisplayMode( display_index, &vm_ideal, &vm_best ) ){
+      vg_error( "SDL_GetClosestDisplayMode failed: %s", SDL_GetError() );
+      exit(0);
+   }
+
+   vg.refresh_rate = vm_best.refresh_rate;
+   vg.window_x = vm_best.w;
+   vg.window_y = vm_best.h;
+#endif
+
+   vg_info( "CreateWindow( %d %d @%.2fhz )\n", vg.window_x, vg.window_y,
+                                               vg.refresh_rate );
 
    /* TODO: Allow selecting closest video mode from launch opts */
    if((vg.window = SDL_CreateWindow( window_name,
+
+#ifdef VG_DEVWINDOW
+         0, 0, vg.window_x, vg.window_y, 
+         SDL_WINDOW_BORDERLESS|SDL_WINDOW_OPENGL|SDL_WINDOW_INPUT_GRABBED
+      ))){}
+#else
                                      SDL_WINDOWPOS_UNDEFINED,
                                      SDL_WINDOWPOS_UNDEFINED,
-                                     mode.w, mode.h,
+                                     vg.window_x, vg.window_y,
 
-                                     SDL_WINDOW_FULLSCREEN | 
+                                     SDL_WINDOW_FULLSCREEN_DESKTOP | 
                                      SDL_WINDOW_OPENGL |
-                                     SDL_WINDOW_INPUT_GRABBED )))
+                                     SDL_WINDOW_INPUT_GRABBED 
+                                     )))
    {
+      if( SDL_SetWindowDisplayMode( vg.window, &vm_best ) ){
+         vg_error( "SDL_SetWindowDisplayMode failed: %s", SDL_GetError() );
+         SDL_Quit();
+         exit(0);
+      }
    }
-   else
-   {
+#endif
+   else{
       vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
       exit(0);
    }
 
+   vg_info( "CreateContext\n" );
+
    /* 
     * OpenGL loading 
     */
@@ -733,41 +759,66 @@ VG_STATIC void vg_init_window(void)
 
    const unsigned char* glver = glGetString( GL_VERSION );
    vg_success( "Load setup complete, OpenGL version: %s\n", glver );
+
+   vg_info( "Setting swap interval\n" );
+
+   if( SDL_GL_SetSwapInterval( -1 ) == -1 )
+   {
+      vg_warn( "Adaptive Vsync not supported\n" );
+
+      if( SDL_GL_SetSwapInterval( 1 ) == -1 )
+      {
+         vg_fatal_exit_loop( "Cannot enable Vsync! You might be overriding it"
+                             " in your graphics control panel.\n" );
+      }
+      else
+         vg_success( "Using vsync\n" );
+   }
+   else
+      vg_success( "Using adaptive Vsync\n" );
+
+   SDL_DisplayMode dispmode;
+   if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) )
+   {
+      if( dispmode.refresh_rate )
+      {
+         vg.refresh_rate = dispmode.refresh_rate;
+         vg_info( "Refresh rate: %d\n", dispmode.refresh_rate );
+      }
+   }
 }
 
 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
 {
-   vg_process_launch_opts_internal();
+   _vg_process_launch_opts_internal( argc, argv );
 
    /* Systems init */
    vg_alloc_quota();
-   vg_log_init();
-   vg_console_init();
-   vg_init_window();
+   _vg_log_init();
+   _vg_console_init();
+   _vg_init_window( window_name );
    
    SDL_SetRelativeMouseMode(1);
    vg.thread_id_main = SDL_GetThreadID(NULL);
    
    /* Opengl-required systems */
-   ui_init_context();
-   vg_loader_init();
+   _vg_ui_init();
+   _vg_loader_init();
 
    vg.engine_status = k_engine_status_running;
 
-   vg_opengl_sync_init();
-   vg_loader_start();
-   
-   /* main */
-   vg_gameloop();
+   _vg_opengl_sync_init();
+    vg_loader_start( _vg_load_full );
+   _vg_gameloop();
 
    /* Shutdown */
-   vg_console_write_persistent();
+   _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_loader_free();
 
    vg_success( "If you see this it means everything went.. \"well\".....\n" );
 
@@ -775,22 +826,8 @@ VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
    SDL_Quit();
 }
 
-/*
- * 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 )
+void vg_print_backtrace(void)
 {
-   /* 
-    *    https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
-    *    thanks gnu <3
-    *
-    *    TODO: this on windows?
-    */
-
 #ifndef _WIN32
 
    void *array[20];
@@ -813,7 +850,25 @@ VG_STATIC void vg_fatal_exit_loop( const char *error )
    free( strings );
 
 #endif
+}
+
+/*
+ * 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 )
+{
+   /* 
+    *    https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
+    *    thanks gnu <3
+    *
+    *    TODO: this on windows?
+    */
 
+   vg_print_backtrace();
    vg_error( "Fatal error: %s\n", error );
 
    SDL_AtomicLock( &vg.sl_context );
@@ -827,47 +882,30 @@ VG_STATIC void vg_fatal_exit_loop( const char *error )
    else
    {
       SDL_AtomicUnlock( &vg.sl_context );
-
-      /* 
-       * if main
-       *    if loader running
-       *       wait until loader checks in, it will die
-       *    else
-       *       pass immediately
-       * else
-       *    if have context
-       *       pass immediately
-       *    else
-       *       wait for main to get to us, it will never be used again
-       *
-       * undefined behaviour:
-       *    fatal_exit_loop is called in both threads, preventing an appropriate
-       *                    reaction to the crash. This *should* be made
-       *                    obvious by the assertion
-       */
+      
       vg_acquire_thread_sync();
 
-      SDL_AtomicUnlock( &vg.sl_context );
+      SDL_AtomicLock( &vg.sl_context );
       vg.engine_status = k_engine_status_crashed;
       vg.str_const_engine_err = error;
+      SDL_AtomicUnlock( &vg.sl_context );
 
-      /* 
-       * Wait for loader to finish what it was doing, if it was running.
-       * Then we can continue in our nice error screen
-       */
-      if( vg.thread_id_main == SDL_GetThreadID(NULL) )
+      /* 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 );
-         SDL_SemWait( vg.sem_loader );
       }
-      else
-         SDL_AtomicUnlock( &vg.sl_context );
 
-      vg_audio_free(NULL);
+      vg_audio_free();
 
       while(1)
       {
-         vg_process_events();
+         _vg_process_events();
          if( vg.window_should_close )
             break;
 
@@ -881,15 +919,15 @@ VG_STATIC void vg_fatal_exit_loop( const char *error )
          glBlendEquation(GL_FUNC_ADD);
 
          glClearColor( 0.15f + sinf(vg.time_real)*0.1f, 0.0f, 0.0f,1.0f );
-         glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+         glClear( GL_COLOR_BUFFER_BIT );
          glViewport( 0,0, vg.window_x, vg.window_y );
 
-         vg_render_log();
+         _vg_render_log();
          SDL_GL_SwapWindow( vg.window );
       }
 
       /* Can now shutdown and EXIT */
-      vg_loader_free();
+      _vg_loader_free();
       SDL_GL_DeleteContext( vg.gl_context );
       SDL_Quit();
       exit(0);