move explicit bezier to vg
[vg.git] / vg_engine.c
index 1723c70f3c26ce22e8a6856e4e0b9a31f828b04e..b07193dfeaa3de13bb0fc95ad8b2601ac8d82642 100644 (file)
@@ -1,12 +1,16 @@
 #include "vg_engine.h"
 #include "vg_async.h"
 
-struct vg_engine vg = { 
+struct vg_engine vg = 
+{ 
    .time_rate = 1.0, 
-   .time_fixed_delta = VG_TIMESTEP_FIXED 
+   .time_fixed_delta = VG_TIMESTEP_FIXED,
 };
 
 #include <string.h>
+#include "vg/vg_ui/imgui.c"
+#include "vg/vg_ui/imgui_impl_opengl.c"
+#include "vg/vg_default_font.gc"
 
 enum engine_status _vg_engine_status(void)
 {
@@ -21,22 +25,21 @@ enum vg_thread_purpose vg_thread_purpose(void)
 {
    SDL_AtomicLock( &vg.sl_status );
 
-   if( vg.thread_id_main == SDL_GetThreadID(NULL) ){
+   SDL_threadID id = SDL_GetThreadID(NULL);
+   if( vg.thread_id_main == id )
+   {
       SDL_AtomicUnlock( &vg.sl_status );
       return k_thread_purpose_main;
    }
-   else{
+   else if( vg.thread_id_loader == id )
+   {
       SDL_AtomicUnlock( &vg.sl_status );
       return k_thread_purpose_loader;
    }
-}
-
-static void vg_assert_thread( enum vg_thread_purpose required )
-{
-   enum vg_thread_purpose purpose = vg_thread_purpose();
-
-   if( purpose != required ){
-      vg_fatal_error( "thread_purpose must be %u not %u\n", required, purpose );
+   else
+   {
+      SDL_AtomicUnlock( &vg.sl_status );
+      return k_thread_purpose_nothing;
    }
 }
 
@@ -47,36 +50,44 @@ static void _vg_opengl_sync_init(void)
 
 #include "vg_console.h"
 #include "vg_profiler.h"
+#include "vg_magi.h"
+#include "vg_mem_view.h"
 #ifndef VG_NO_AUDIO
   #include "vg_audio.h"
 #endif
 #include "vg_shader.h"
 #include "vg_tex.h"
 #include "vg_input.h"
-#include "vg_imgui.h"
+#include "vg_framebuffer.h"
+#include "vg_render.h"
 #include "vg_lines.h"
 #include "vg_rigidbody_view.h"
 #include "vg_loader.h"
 #include "vg_opt.h"
+#include "vg/vg_ui/imgui.h"
 
 /* Diagnostic */
 static struct vg_profile vg_prof_update = {.name="update()"},
                          vg_prof_render = {.name="render()"},
                          vg_prof_swap   = {.name="swap"};
 
-void vg_checkgl( const char *src_info )
+static f64 _vg_gameloop_budget() 
 {
-   int fail = 0;
+   int frame_target = vg.display_refresh_rate;
+   if( vg.fps_limit > 0 ) frame_target = vg.fps_limit;
+   return (1.0/(f64)frame_target)*1000.0;
+}
 
-   GLenum err;
-   while( (err = glGetError()) != GL_NO_ERROR ){
-      vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
-      fail = 1;
+struct vg_profile_set static _vg_prof_gameloop = 
+{
+   .name = "gameloop",
+   .get_budget = _vg_gameloop_budget,
+   .count = 3,
+   .list = 
+   {
+      &vg_prof_update, &vg_prof_render, &vg_prof_swap
    }
-
-   if( fail )
-      vg_fatal_error( "OpenGL Error" );
-}
+};
 
 static void async_vg_bake_shaders( void *payload, u32 size )
 {
@@ -92,17 +103,18 @@ void vg_bake_shaders(void)
 void async_internal_complete( void *payload, u32 size )
 {
    vg_success( "Internal async setup complete\n" );
-   SDL_AtomicLock( &vg.sl_status );
 
-   if( vg.engine_status == k_engine_status_crashed ){
+   SDL_AtomicLock( &vg.sl_status );
+   if( vg.engine_status == k_engine_status_crashed )
+   {
       SDL_AtomicUnlock( &vg.sl_status );
       return;
    }
-   else{
+   else
       vg.engine_status = k_engine_status_running;
-   }
-
    SDL_AtomicUnlock( &vg.sl_status );
+
+   vg_magi_restore();
 }
 
 #ifdef VG_CUSTOM_SHADERS
@@ -111,19 +123,32 @@ void vg_auto_shader_register(void); /* created from codegen */
 
 static void _vg_load_full( void *data )
 {
+vg_info(" Copyright  .        . .       -----, ,----- ,---.   .---.  \n" );
+vg_info(" 2021-2024  |\\      /| |           /  |      |    | |    /| \n" );
+vg_info("            | \\    / | +--        /   +----- +---'  |   / | \n" );
+vg_info("            |  \\  /  | |         /    |      |   \\  |  /  | \n" );
+vg_info("            |   \\/   | |        /     |      |    \\ | /   | \n" );
+vg_info("            '        ' '--' [] '----- '----- '     ' '---'  " 
+        "SOFTWARE\n" );
+
    vg_preload();
    vg_tex2d_replace_with_error_async( &vg.tex_missing );
    vg_async_stall();
    vg_ui.tex_bg = vg.tex_missing;
 
    /* internal */
+   vg_loader_step( vg_framebuffer_init, NULL );
+   vg_loader_step( vg_render_init, NULL );
    vg_loader_step( vg_input_init, vg_input_free );
    vg_loader_step( vg_lines_init, NULL );
    vg_loader_step( vg_rb_view_init, NULL );
+   _vg_profile_reg_set( &_vg_prof_gameloop );
+
 #ifndef VG_NO_AUDIO
    vg_loader_step( vg_audio_init, vg_audio_free );
 #endif
    vg_loader_step( vg_profiler_init, NULL );
+   vg_loader_step( vg_mem_view_init, NULL );
 
    /* client */
 #ifdef VG_CUSTOM_SHADERS
@@ -141,42 +166,51 @@ static void _vg_process_events(void)
    v2_zero( vg.mouse_wheel );
    v2_zero( vg.mouse_delta );
 
-   /* Update input */
-   vg_process_inputs();
-
    /* SDL event loop */
    SDL_Event event;
-   while( SDL_PollEvent( &event ) ){
-      if( event.type == SDL_KEYDOWN ){
+   while( SDL_PollEvent( &event ) )
+   {
+      if( event.type == SDL_KEYDOWN )
+      {
          if( vg_console.enabled && 
-               (vg_ui.focused_control_type != k_ui_control_modal) ){
+               (vg_ui.ctx.focused_control_type != k_ui_control_modal) )
+         {
             if( event.key.keysym.sym == SDLK_ESCAPE ||
-                event.key.keysym.scancode == SDL_SCANCODE_GRAVE ){
+                event.key.keysym.scancode == SDL_SCANCODE_GRAVE )
+            {
                vg_console.enabled = 0;
-               ui_defocus_all();
+               ui_defocus_all( &vg_ui.ctx );
             }
             else if( (event.key.keysym.mod & KMOD_CTRL) && 
-                      event.key.keysym.sym == SDLK_n ){
-               console_suggest_next();
+                      event.key.keysym.sym == SDLK_n )
+            {
+               console_suggest_next( &vg_ui.ctx );
             }
             else if( (event.key.keysym.mod & KMOD_CTRL ) &&
-                      event.key.keysym.sym == SDLK_p ){
-               console_suggest_prev();
+                      event.key.keysym.sym == SDLK_p )
+            {
+               console_suggest_prev( &vg_ui.ctx );
             }
-            else{
-               ui_proc_key( event.key.keysym );
+            else
+            {
+               vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym );
             }
          }
-         else{
-            if( event.key.keysym.scancode == SDL_SCANCODE_GRAVE ){
+         else
+         {
+            if( event.key.keysym.scancode == SDL_SCANCODE_GRAVE )
+            {
+               vg_console.auto_focus = 1;
                vg_console.enabled = 1;
             }
-            else {
-               ui_proc_key( event.key.keysym );
+            else 
+            {
+               vg_ui_handle_sdl_key( &vg_ui.ctx, event.key.keysym );
             }
          }
       }
-      else if( event.type == SDL_MOUSEWHEEL ){
+      else if( event.type == SDL_MOUSEWHEEL )
+      {
          vg.mouse_wheel[0] += event.wheel.preciseX;
          vg.mouse_wheel[1] += event.wheel.preciseY;
       }
@@ -191,36 +225,47 @@ static void _vg_process_events(void)
       {
          vg_input_controller_event( &event );
       }
-      else if( event.type == SDL_MOUSEMOTION ){
+      else if( event.type == SDL_MOUSEMOTION )
+      {
          vg.mouse_delta[0] += event.motion.xrel;
          vg.mouse_delta[1] += event.motion.yrel;
       }
-      else if( event.type == SDL_WINDOWEVENT ){
-         if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ){
+      else if( event.type == SDL_WINDOWEVENT )
+      {
+         if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED )
+         {
             int w, h;
             SDL_GL_GetDrawableSize( vg.window, &w, &h );
 
-            if( !w || !h ){
+            if( !w || !h )
+            {
                vg_warn( "Got a invalid framebuffer size: "
                         "%dx%d... ignoring\n", w, h );
             }
-            else{
+            else
+            {
+
+               i32 delta[2] = { w - vg.window_x, h - vg.window_y };
+               _vg_magi_area_change( delta );
                vg.window_x = w;
                vg.window_y = h;
 
-               vg_framebuffer_resize(w,h);
+               vg_framebuffer_update_sizes();
             }
          }
-         else if( event.window.event == SDL_WINDOWEVENT_CLOSE ){
+         else if( event.window.event == SDL_WINDOWEVENT_CLOSE )
+         {
             vg.window_should_close = 1;
          }
       }
-      else if( event.type == SDL_TEXTINPUT ){
-         ui_proc_utf8( event.text.text );
+      else if( event.type == SDL_TEXTINPUT )
+      {
+         ui_proc_utf8( &vg_ui.ctx, event.text.text );
       }
    }
 
-   SDL_GetMouseState( &vg.mouse_pos[0], &vg.mouse_pos[1] );
+   vg.mouse_state = SDL_GetMouseState( &vg.mouse_pos[0], &vg.mouse_pos[1] );
+   vg_process_inputs();
 }
 
 static void _vg_gameloop_update(void)
@@ -255,7 +300,7 @@ static void _vg_gameloop_update(void)
    vg_profile_end( &vg_prof_update );
 }
 
-static void vg_settings_gui(void);
+static void vg_settings_gui( ui_context *ctx );
 static void _vg_gameloop_render(void)
 {
    vg_profile_begin( &vg_prof_render );
@@ -269,87 +314,74 @@ static void _vg_gameloop_render(void)
    /* ui */
    vg.engine_stage = k_engine_stage_ui;
    {
-      ui_prerender();
-      if( vg_console.enabled ){ 
-         vg_ui.ignore_input_frames = 10;
-         vg_gui();
-         vg_ui.ignore_input_frames = 0;
-         vg_ui.wants_mouse = 1;
-         vg_console_draw();
+      ui_prerender( &vg_ui.ctx );
+      vg_ui_set_screen( vg.window_x, vg.window_y );
+      ui_update_mouse( &vg_ui.ctx, 
+         (ui_px[2]){ vg.mouse_pos[0], vg.mouse_pos[1] }, vg.mouse_state );
+
+      vg_framebuffer_ui( &vg_ui.ctx );
+
+      if( vg_console.enabled )
+      { 
+         ui_ignore_input_frames( &vg_ui.ctx, 10 );
+         vg_gui( &vg_ui.ctx );
+         ui_ignore_input_frames( &vg_ui.ctx, 0 );
+         ui_capture_mouse( &vg_ui.ctx, 1 );
+         vg_console_draw( &vg_ui.ctx );
       }
-      else vg_gui();
+      else vg_gui( &vg_ui.ctx );
 
       if( vg.settings_open )
-         vg_settings_gui();
-
-      /* vg tools */
-#ifndef VG_NO_AUDIO
-      audio_debug_ui( vg.pv );
-#endif
+         vg_settings_gui( &vg_ui.ctx );
+      
+      _vg_magi_render( &vg_ui.ctx );
 
-               /* profiling */
-      if( vg_profiler ){
-         int frame_target = vg.display_refresh_rate;
-         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, 0
-         );
-         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"
-               "      extrap(%.2f) frame(%.2f) spin( "PRINTF_U64" )\n",
-               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_real, vg.time_delta, vg.time_rate,
-               vg.time_fixed_extrapolate, vg.time_frame_delta,
-               vg.time_spinning );
-
-         ui_text( (ui_rect){258,4,900,900},perf,1,0,k_ui_align_left);
-      }
-      ui_postrender();
+      ui_postrender( &vg_ui.ctx, vg.time_frame_delta );
+      vg_ui_post_update();
    }
 }
 
-static void vg_changevsync(void){
-   if( vg.vsync && (vg.vsync_feature != k_vsync_feature_error) ){
+static void vg_changevsync(void)
+{
+   if( vg.vsync && (vg.vsync_feature != k_vsync_feature_error) )
+   {
       /* turn on vsync if not enabled */
 
       enum vsync_feature requested = k_vsync_feature_enabled;
       if( vg.vsync < 0 ) requested = k_vsync_feature_enabled_adaptive;
 
-      if( vg.vsync_feature != requested ){
+      if( vg.vsync_feature != requested )
+      {
          vg_info( "Setting swap interval\n" );
 
          int swap_interval = 1;
          if( requested == k_vsync_feature_enabled_adaptive ) 
             swap_interval = -1;
 
-         if( SDL_GL_SetSwapInterval( swap_interval ) == -1 ){
-            if( requested == k_vsync_feature_enabled ){
-               vg_error( "Vsync is not supported by your system\n" );
-               vg_warn( "You may be overriding it in your"
-                        " graphics control panel.\n" );
+         if( SDL_GL_SetSwapInterval( swap_interval ) == -1 )
+         {
+            if( requested == k_vsync_feature_enabled )
+            {
+               ui_start_modal( &vg_ui.ctx, 
+                     "Vsync not supported on this system.\n\n"
+                     "You may be overriding it in your"
+                     " graphics \ncontrol panel.\n",
+                     UI_MODAL_BAD );
             }
-            else{
-               vg_error( "Adaptive Vsync is not supported by your system\n" );
+            else
+            {
+               ui_start_modal( &vg_ui.ctx, 
+                     "Adaptive Vsync is not supported by your system\n\n"
+                     "You may be overriding it in your"
+                     " graphics \ncontrol panel.\n",
+                     UI_MODAL_BAD );
             }
 
             vg.vsync_feature = k_vsync_feature_error;
             vg.vsync = 0;
-            /* TODO: Make popup to notify user that this happened */
          }
-         else{
+         else
+         {
             vg_success( "Vsync enabled (%d)\n", requested );
             vg.vsync_feature = requested;
          }
@@ -389,38 +421,44 @@ static int vg_framefilter( double dt ){
    return 0;
 }
 
-static int _vg_crashscreen(void)
+static void _vg_crashscreen(void)
 {
-#if 0
-   if( vg_getkey( SDLK_ESCAPE ) )
-      return 1;
-#endif
-
    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_real)*0.1f, 0.0f, 0.0f,1.0f );
+   glClearColor( 0.15f,0.0f,0.0f,1.0f );
    glClear( GL_COLOR_BUFFER_BIT );
    glViewport( 0,0, vg.window_x, vg.window_y );
 
-#if 0
-   _vg_render_log();
-#endif
+   ui_prerender( &vg_ui.ctx );
+   vg_ui_set_screen( vg.window_x, vg.window_y );
+   ui_update_mouse( &vg_ui.ctx, 
+      (ui_px[2]){ vg.mouse_pos[0], vg.mouse_pos[1] }, vg.mouse_state );
 
-   return 0;
-}
+   vg_framebuffer_ui( &vg_ui.ctx );
 
-static void _vg_gameloop(void){
-   //vg.time_fixed_accumulator = 0.75f * (1.0f/60.0f);
+   vg_console.enabled = 1;
+   ui_ignore_input_frames( &vg_ui.ctx, 10 );
+   vg_gui( &vg_ui.ctx );
+   ui_ignore_input_frames( &vg_ui.ctx, 0 );
+   ui_capture_mouse( &vg_ui.ctx, 1 );
+   vg_console_draw( &vg_ui.ctx );
 
+   ui_postrender( &vg_ui.ctx, vg.time_frame_delta );
+   vg_ui_post_update();
+}
+
+static void _vg_gameloop(void)
+{
    vg.time_hp = SDL_GetPerformanceCounter();
    vg.time_hp_last = vg.time_hp;
 
    int post_start = 0;
-   while(1){
+   while(1)
+   {
       vg.time_hp = SDL_GetPerformanceCounter();
       u64 udt = vg.time_hp - vg.time_hp_last;
       vg.time_hp_last = vg.time_hp;
@@ -453,21 +491,25 @@ static void _vg_gameloop(void){
       if( vg.window_should_close )
          break;
          
-      if( status == k_engine_status_crashed ){
-         if( _vg_crashscreen() )
-            break;
+      if( status == k_engine_status_crashed )
+      {
+         _vg_crashscreen();
       }
-      else{
-         if( status == k_engine_status_running ){
+      else
+      {
+         if( status == k_engine_status_running )
+         {
             _vg_gameloop_update();
             _vg_gameloop_render();
          }
-         else{
+         else
+         {
             vg_loader_render();
          }
       }
 
-      if( vg.loader_ring > 0.01f ){
+      if( vg.loader_ring > 0.01f )
+      {
          vg_loader_render_ring( vg.loader_ring );
          vg.loader_ring -= vg.time_frame_delta * 0.5f;
       }
@@ -480,28 +522,30 @@ static void _vg_gameloop(void){
 static void _vg_process_launch_opts_internal( int argc, char *argv[] )
 {
    char *arg;
-   while( vg_argp( argc, argv ) ){
-      if( (arg = vg_opt_arg( 'w' )) ){
+   while( vg_argp( argc, argv ) )
+   {
+      if( (arg = vg_opt_arg( 'w', "Render output width" )) )
          vg.window_x = atoi( arg );
-      }
 
-      if( (arg = vg_opt_arg( 'h' )) ){
+      if( (arg = vg_opt_arg( 'h', "Render output height" )) )
          vg.window_y = atoi( arg );
-      }
 
-      if( (arg = vg_long_opt_arg( "samples" )) ){
+      if( (arg = vg_long_opt_arg( "samples", "Rendering samples per pixel" )) )
          vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
-      }
 
-      if(  vg_long_opt( "use-libc-malloc" ) ){
+      if(  vg_long_opt( "use-libc-malloc", "Use standard libc allocator" ) )
          vg_mem.use_libc_malloc = 1;
-      }
 
-      if( vg_long_opt( "high-performance" ) ){
+      if( vg_long_opt( "high-performance", "Turn graphics to lowest quality" ) )
          vg.quality_profile = k_quality_profile_low;
-      }
 
       vg_launch_opt();
+
+      if( vg_long_opt( "help", "Helps you" ) )
+      {
+         _vg_print_options();
+         exit(0);
+      }
    }
 }
 
@@ -509,7 +553,8 @@ static void _vg_init_window( const char *window_name )
 {
    vg_info( "SDL_INIT\n" );
 
-   if( SDL_Init( SDL_INIT_VIDEO ) != 0  ){
+   if( SDL_Init( SDL_INIT_VIDEO ) != 0  )
+   {
       vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
       exit(0);
    }
@@ -552,7 +597,8 @@ static void _vg_init_window( const char *window_name )
        mode_index = 0;
 
    SDL_DisplayMode video_mode;
-   if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) ){
+   if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) )
+   {
       vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() );
       SDL_Quit();
       exit(0);
@@ -562,7 +608,8 @@ static void _vg_init_window( const char *window_name )
    vg.window_x = video_mode.w;
    vg.window_y = video_mode.h;
 
-   if( vg.screen_mode == 2 ){
+   if( vg.screen_mode == 2 )
+   {
       vg.window_x = 1280;
       vg.window_y = 720;
    }
@@ -584,11 +631,13 @@ static void _vg_init_window( const char *window_name )
    vg_info( "CreateWindow( %d %d %u )\n", vg.window_x, vg.window_y, flags );
 
    if((vg.window = SDL_CreateWindow( window_name, 0, 0, 
-                                     vg.window_x, vg.window_y, flags ))){
+                                     vg.window_x, vg.window_y, flags )))
+   {
       if( vg.screen_mode == 2 )
          SDL_SetWindowPosition( vg.window, video_mode.w-vg.window_x, 0 );
    }
-   else{
+   else
+   {
       vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
       exit(0);
    }
@@ -605,17 +654,20 @@ static void _vg_init_window( const char *window_name )
    /* 
     * OpenGL loading 
     */
-   if( (vg.gl_context = SDL_GL_CreateContext(vg.window) )){
+   if( (vg.gl_context = SDL_GL_CreateContext(vg.window) ))
+   {
       SDL_GL_GetDrawableSize( vg.window, &vg.window_x, &vg.window_y );
       vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
    }
-   else{
+   else
+   {
       vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
       SDL_Quit();
       exit(0);
    }
 
-   if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) {
+   if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) 
+   {
       vg_error( "Glad Failed to initialize\n" );
       SDL_GL_DeleteContext( vg.gl_context );
       SDL_Quit();
@@ -628,38 +680,49 @@ static void _vg_init_window( const char *window_name )
    SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
 
    SDL_DisplayMode dispmode;
-   if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) ){
-      if( dispmode.refresh_rate ){
+   if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) )
+   {
+      if( dispmode.refresh_rate )
+      {
          vg.display_refresh_rate = dispmode.refresh_rate;
       }
    }
 
-   if( vg.display_refresh_rate < 25 || vg.display_refresh_rate > 300 ){
+   if( vg.display_refresh_rate < 25 || vg.display_refresh_rate > 300 )
+   {
       vg.display_refresh_rate = 60;
    }
 
    vg_info( "Display refresh rate: %d\n", dispmode.refresh_rate );
-   if( !vg.fps_limit) vg.fps_limit = vg.display_refresh_rate;
+   if( !vg.fps_limit ) 
+      vg.fps_limit = vg.display_refresh_rate;
 }
 
 static void _vg_terminate(void)
 {
    /* Shutdown */
+   vg_magi_save();
    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" );
+   vg_loader_atexit();
 
    SDL_GL_DeleteContext( vg.gl_context );
    SDL_Quit();
+
+   vg_success( "The program has terminated 'correctly'\n" );
    exit(0);
 }
 
+static int cmd_log_memory( int argc, const char *argv[] )
+{
+   vg_mem_log( vg_mem.rtmemory, 0, "rtmemory" );
+   return 0;
+}
+
 static int cmd_vg_settings_toggle( int argc, const char *argv[] );
 void vg_enter( int argc, char *argv[], const char *window_name )
 {
@@ -669,6 +732,7 @@ void vg_enter( int argc, char *argv[], const char *window_name )
    /* Systems init */
    vg_alloc_quota();
    vg_console_init();
+   vg_magi_init();
    
    vg_console_reg_var( "vg_fps_limit", &vg.fps_limit, 
                         k_var_dtype_i32, VG_VAR_PERSISTENT );
@@ -678,10 +742,12 @@ void vg_enter( int argc, char *argv[], const char *window_name )
                         k_var_dtype_i32, VG_VAR_PERSISTENT );
    vg_console_reg_var( "vg_screen_mode", &vg.screen_mode,
                         k_var_dtype_i32, VG_VAR_PERSISTENT );
+
    vg_audio_register();
    vg_console_load_autos();
 
    vg_console_reg_cmd( "vg_settings", cmd_vg_settings_toggle, NULL );
+   vg_console_reg_cmd( "vg_rtmemory", cmd_log_memory, NULL );
    _vg_init_window( window_name );
 
    vg_async_init();
@@ -691,24 +757,41 @@ void vg_enter( int argc, char *argv[], const char *window_name )
    
    /* Opengl-required systems */
    vg_ui_init();
-   vg_loader_init();
 
    vg.engine_status = k_engine_status_load_internal;
+   vg_loader_step( vg_loader_init, vg_loader_free );
 
    _vg_opengl_sync_init();
-    vg_loader_start( _vg_load_full, NULL );
+    vg_loader_start( _vg_load_full, NULL );  
    _vg_gameloop();
    _vg_terminate();
 }
 
-void vg_fatal_error( const char *fmt, ... )
+#ifndef _WIN32
+ #include <execinfo.h>
+#endif
+
+void vg_fatal_exit(void)
 {
-   va_list args;
-   va_start( args, fmt );
-   _vg_logx_va( stderr, NULL, "fatal", KRED, fmt, args );
-   va_end( args );
+   /* append backtrace */
+#if !defined(_WIN32)
+   void *array[20];
+   char **strings;
+   int size, i;
 
-   vg_print_backtrace();
+   size = backtrace( array, 20 );
+   strings = backtrace_symbols( array, size );
+
+   if( strings != NULL )
+   {
+      vg_error( "\n---------------- gnu backtrace -------------\n" );
+
+      for( int i=0; i<size; i++ )
+         vg_low( "%s\n", strings[i] );
+   }
+
+   free( strings );
+#endif
 
    SDL_AtomicLock( &vg.sl_status );
    vg.engine_status = k_engine_status_crashed;
@@ -720,18 +803,30 @@ void vg_fatal_error( const char *fmt, ... )
    }
    else
    {
-      vg_error( "There is no jump to the error runner thing yet! bai bai\n" );
+      /* TODO: This should also be able to go to the crash screen? */
       _vg_terminate();
    }
 }
 
+void vg_fatal_error( const char *fmt, ... )
+{
+   vg_fatal_condition();
+
+   va_list args;
+   va_start( args, fmt );
+   _vg_logx_va( stderr, NULL, "fatal", KRED, fmt, args );
+   va_end( args );
+
+   vg_fatal_exit();
+}
+
 /* 
  * settings menu
  * ---------------------------------------------------------------------------
  */
 
 #ifdef VG_GAME_SETTINGS
-extern void vg_game_settings_gui( ui_rect panel ) ;
+extern void vg_game_settings_gui( ui_context *ctx, ui_rect panel ) ;
 extern void vg_game_settings_init(void);
 #endif 
 
@@ -783,56 +878,66 @@ static vg_settings = {
              .options = vg_settings_dsp_enum, .option_count=2 },
 };
 
-static void vg_settings_ui_draw_diff( ui_rect orig ){
+static void vg_settings_ui_draw_diff( ui_context *ctx, ui_rect orig )
+{
    ui_rect l,r;
    ui_split( orig, k_ui_axis_v, -32, 0, l, r );
-   ui_text( r, "*", 1, k_ui_align_middle_center, ui_colour(k_ui_blue) );
+   ui_text( ctx, r, "*", 1, 
+            k_ui_align_middle_center, ui_colour(ctx, k_ui_blue) );
 }
 
 /* i32 settings 
  * ------------------------------------------------------------------------- */
 
-static void vg_settings_ui_int( char *buf, u32 len ){
-   for( u32 i=0, j=0; i<len; i ++ ){
+static void vg_settings_ui_int( ui_context *ctx, char *buf, u32 len )
+{
+   for( u32 i=0, j=0; i<len; i ++ )
+   {
       if( ((buf[i] >= '0') && (buf[i] <= '9')) || (buf[i] == '\0') )
          buf[j ++] = buf[i];
    }
 }
 
-struct ui_textbox_callbacks static vg_settings_ui_int_callbacks = {
+struct ui_textbox_callbacks static vg_settings_ui_int_callbacks = 
+{
    .change = vg_settings_ui_int
 };
 
-static bool vg_settings_ranged_i32_valid( struct vg_setting_ranged_i32 *prop ){
+static bool vg_settings_ranged_i32_valid( struct vg_setting_ranged_i32 *prop )
+{
    if( prop->new_value < prop->min ) return 0;
    if( prop->new_value > prop->max ) return 0;
    return 1;
 }
 
-static bool vg_settings_ranged_i32_diff( struct vg_setting_ranged_i32 *prop ){
+static bool vg_settings_ranged_i32_diff( struct vg_setting_ranged_i32 *prop )
+{
    if( prop->new_value != *prop->actual_value ) return 1;
    else return 0;
 }
 
-static bool vg_settings_ui_ranged_i32( struct vg_setting_ranged_i32 *prop, 
-                                       ui_rect rect ){
+static bool vg_settings_ui_ranged_i32( ui_context *ctx,
+                                       struct vg_setting_ranged_i32 *prop, 
+                                       ui_rect rect )
+{
    ui_rect orig;
    rect_copy( rect, orig );
 
-   ui_textbox( rect, prop->label, prop->buf, sizeof(prop->buf), 
+   ui_textbox( ctx, rect, prop->label, prop->buf, sizeof(prop->buf), 
                1, 0, &vg_settings_ui_int_callbacks );
    prop->new_value = atoi( prop->buf );
 
    if( vg_settings_ranged_i32_diff( prop ) )
-      vg_settings_ui_draw_diff( orig );
+      vg_settings_ui_draw_diff( ctx, orig );
 
    bool valid = vg_settings_ranged_i32_valid( prop );
-   if( !valid ){
+   if( !valid )
+   {
       ui_rect _null, line;
       ui_split( orig, k_ui_axis_h, -1, 0, _null, line );
       line[1] += 3;
 
-      ui_fill( line, ui_colour( k_ui_red ) );
+      ui_fill( ctx, line, ui_colour( ctx, k_ui_red ) );
    }
 
    return valid;
@@ -855,16 +960,17 @@ bool vg_settings_enum_diff( struct vg_setting_enum *prop )
    else return 0;
 }
 
-bool vg_settings_enum( struct vg_setting_enum *prop, ui_rect rect )
+bool vg_settings_enum( ui_context *ctx,
+                       struct vg_setting_enum *prop, ui_rect rect )
 {
    ui_rect orig;
    rect_copy( rect, orig );
 
-   ui_enum( rect, prop->label,
+   ui_enum( ctx, rect, prop->label,
             prop->options, prop->option_count, &prop->new_value );
 
    if( vg_settings_enum_diff( prop ) )
-      vg_settings_ui_draw_diff( orig );
+      vg_settings_ui_draw_diff( ctx, orig );
    
    return 1;
 }
@@ -876,50 +982,60 @@ void ui_settings_enum_init( struct vg_setting_enum *prop )
 
 /* .. */
 
-void vg_settings_ui_header( ui_rect inout_panel, const char *name )
+void vg_settings_ui_header( ui_context *ctx,
+                            ui_rect inout_panel, const char *name )
 {
    ui_rect rect;
-   ui_standard_widget( inout_panel, rect, 2 );
-   ui_text( rect, name, 1, k_ui_align_middle_center, ui_colour(k_ui_fg+3) );
+   ui_standard_widget( ctx, inout_panel, rect, 2 );
+   ui_text( ctx, rect, name, 1, 
+            k_ui_align_middle_center, ui_colour(ctx, k_ui_fg+3) );
 }
 
 
-bool vg_settings_apply_button( ui_rect inout_panel, bool validated )
+bool vg_settings_apply_button( ui_context *ctx, 
+                               ui_rect inout_panel, bool validated )
 {
    ui_rect last_row;
-   ui_px height = (vg_ui.font->sy + 18) * k_ui_scale;
-   ui_split( inout_panel, k_ui_axis_h, -height, k_ui_padding, 
+   ui_px height = ui_standard_widget_height( ctx, 1 );
+   ui_split( inout_panel, k_ui_axis_h, -height, 8,
              inout_panel, last_row );
 
    const char *string = "Apply";
-   if( validated ){
-      if( ui_button( last_row, string ) == 1 )
+   if( validated )
+   {
+      if( ui_button( ctx, last_row, string ) == 1 )
          return 1;
    }
-   else{
+   else
+   {
       ui_rect rect;
-      ui_standard_widget( last_row, rect, 1 );
-      ui_fill( rect, ui_colour( k_ui_bg+1 ) );
-      ui_outline( rect, -1, ui_colour( k_ui_red ), 0 );
+      ui_standard_widget( ctx, last_row, rect, 1 );
+      ui_fill( ctx, rect, ui_colour( ctx, k_ui_bg+1 ) );
+      ui_outline( ctx, rect, -1, ui_colour( ctx, k_ui_red ), 0 );
 
-      ui_rect t = { 0,0, ui_text_line_width( string ), 14 };
+      ui_rect t = { 0,0, ui_text_line_width( ctx, string ), 14 };
       ui_rect_center( rect, t );
-      ui_text( t, string, 1, k_ui_align_left, ui_colour(k_ui_fg+3) );
+      ui_text( ctx, t, string, 1, k_ui_align_left, ui_colour(ctx,k_ui_fg+3) );
    }
 
    return 0;
 }
 
-static void vg_settings_video_apply(void){
-   if( vg_settings_enum_diff( &vg_settings.screenmode ) ){
+static void vg_settings_video_apply(void)
+{
+   if( vg_settings_enum_diff( &vg_settings.screenmode ) )
+   {
       vg.screen_mode = vg_settings.screenmode.new_value;
 
-      if( (vg.screen_mode == 0) || (vg.screen_mode == 1) ){
+      if( (vg.screen_mode == 0) || (vg.screen_mode == 1) )
+      {
          SDL_DisplayMode video_mode;
-         if( SDL_GetDesktopDisplayMode( 0, &video_mode ) ){
+         if( SDL_GetDesktopDisplayMode( 0, &video_mode ) )
+         {
             vg_error("SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError());
          }
-         else {
+         else 
+         {
             //vg.display_refresh_rate = video_mode.refresh_rate;
             vg.window_x = video_mode.w;
             vg.window_y = video_mode.h;
@@ -931,7 +1047,8 @@ static void vg_settings_video_apply(void){
          SDL_SetWindowFullscreen( vg.window, SDL_WINDOW_FULLSCREEN_DESKTOP );
       if( vg.screen_mode == 1 )
          SDL_SetWindowFullscreen( vg.window, SDL_WINDOW_FULLSCREEN );
-      if( vg.screen_mode == 2 ){
+      if( vg.screen_mode == 2 )
+      {
          SDL_SetWindowFullscreen( vg.window, 0 );
          SDL_SetWindowSize( vg.window, 1280, 720 );
          SDL_SetWindowPosition( vg.window, 16, 16 );
@@ -945,11 +1062,12 @@ static void vg_settings_video_apply(void){
    vg.vsync = vg_settings.vsync.new_value;
 }
 
-static void vg_settings_video_gui( ui_rect panel ){
+static void vg_settings_video_gui( ui_context *ctx, ui_rect panel )
+{
    bool validated = 1;
    ui_rect rq;
-   ui_standard_widget( panel, rq, 1 );
-   vg_settings_enum( &vg_settings.quality, rq );
+   ui_standard_widget( ctx, panel, rq, 1 );
+   vg_settings_enum( ctx, &vg_settings.quality, rq );
 
    /* FIXME */
 #if 0
@@ -959,41 +1077,49 @@ static void vg_settings_video_gui( ui_rect panel ){
 #endif
 
    /* frame timing */
-   vg_settings_ui_header( panel, "Frame Timing" );
+   vg_settings_ui_header( ctx, panel, "Frame Timing" );
    ui_rect duo, d0,d1;
-   ui_standard_widget( panel, duo, 1 );
+   ui_standard_widget( ctx, panel, duo, 1 );
    ui_split_ratio( duo, k_ui_axis_v, 0.5f, 16, d0, d1 );
 
-   vg_settings_enum( &vg_settings.vsync, d0 );
-   validated &= vg_settings_ui_ranged_i32( &vg_settings.fps_limit, d1 );
+   vg_settings_enum( ctx, &vg_settings.vsync, d0 );
+   validated &= vg_settings_ui_ranged_i32( ctx, &vg_settings.fps_limit, d1 );
 
    /* profiler */
-   ui_standard_widget( panel, duo, 10 );
+   ui_standard_widget( ctx, panel, duo, 10 );
    int frame_target = vg.display_refresh_rate;
    if( !vg.vsync ) frame_target = vg.fps_limit;
    vg_profile_drawn( 
-         (struct vg_profile *[]){
-            &vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3,
+         ctx,
+         (struct vg_profile *[])
+         {
+            &vg_prof_update,
+            &vg_prof_render,
+            &vg_prof_swap
+         }, 3,
          (1.0f/(f32)frame_target)*1500.0f, 
          duo, 1, 0
    );
 
-   ui_fill( (ui_rect){ duo[0], duo[1]+(duo[3]*2)/3, duo[2], 1 },
-             ui_colour(k_ui_fg) );
+   ui_fill( ctx, (ui_rect){ duo[0], duo[1]+(duo[3]*2)/3, duo[2], 1 },
+             ui_colour(ctx, k_ui_fg) );
 
    /* window spec */
-   vg_settings_ui_header( panel, "Window Specification" );
+   vg_settings_ui_header( ctx, panel, "Window Specification" );
 
-   ui_standard_widget( panel, duo, 1 );
-   vg_settings_enum( &vg_settings.screenmode, duo );
+   ui_standard_widget( ctx, panel, duo, 1 );
+   vg_settings_enum( ctx, &vg_settings.screenmode, duo );
 
-   if( vg_settings_apply_button( panel, validated ) )
+   if( vg_settings_apply_button( ctx, panel, validated ) )
       vg_settings_video_apply();
 }
 
-static void vg_settings_audio_apply(void){
-   if( vg_settings_enum_diff( &vg_settings.audio_devices ) ){
-      if( vg_audio.sdl_output_device ){
+static void vg_settings_audio_apply(void)
+{
+   if( vg_settings_enum_diff( &vg_settings.audio_devices ) )
+   {
+      if( vg_audio.sdl_output_device )
+      {
          vg_info( "Closing audio device %d\n", vg_audio.sdl_output_device );
          SDL_CloseAudioDevice( vg_audio.sdl_output_device );
       }
@@ -1001,16 +1127,20 @@ static void vg_settings_audio_apply(void){
       vg_strfree( &vg_audio.device_choice );
 
       if( vg_settings.audio_devices.new_value == -1 ){ }
-      else if( vg_settings.audio_devices.new_value == -2 ){
-         vg_fatal_error( "Programming error\n" );
+      else if( vg_settings.audio_devices.new_value == -2 )
+      {
+         vg_fatal_exit();
       }
-      else {
+      else 
+      {
          struct ui_enum_opt *selected = NULL, *oi;
 
-         for( int i=0; i<vg_settings.audio_devices.option_count; i ++ ){
+     for( int i=0; i<vg_settings.audio_devices.option_count; i ++ )
+         {
             oi = &vg_settings.audio_devices.options[i];
 
-            if( oi->value == vg_settings.audio_devices.new_value ){
+            if( oi->value == vg_settings.audio_devices.new_value )
+            {
                selected = oi;
                break;
             }
@@ -1026,7 +1156,8 @@ static void vg_settings_audio_apply(void){
    }
 
    audio_lock();
-   if( vg_settings_enum_diff( &vg_settings.dsp ) ){
+   if( vg_settings_enum_diff( &vg_settings.dsp ) )
+   {
       *vg_settings.dsp.actual_value =
          vg_settings.dsp.new_value;
    }
@@ -1034,15 +1165,16 @@ static void vg_settings_audio_apply(void){
    audio_unlock();
 }
 
-static void vg_settings_audio_gui( ui_rect panel ){
+static void vg_settings_audio_gui( ui_context *ctx, ui_rect panel )
+{
    ui_rect rq;
-   ui_standard_widget( panel, rq, 1 );
-   vg_settings_enum( &vg_settings.audio_devices, rq );
+   ui_standard_widget( ctx, panel, rq, 1 );
+   vg_settings_enum( ctx, &vg_settings.audio_devices, rq );
    
-   ui_standard_widget( panel, rq, 1 );
-   vg_settings_enum( &vg_settings.dsp, rq );
+   ui_standard_widget( ctx, panel, rq, 1 );
+   vg_settings_enum( ctx, &vg_settings.dsp, rq );
 
-   if( vg_settings_apply_button( panel, 1 ) )
+   if( vg_settings_apply_button( ctx, panel, 1 ) )
       vg_settings_audio_apply();
 }
 
@@ -1110,27 +1242,27 @@ void vg_settings_close(void)
    free( vg_settings.audio_devices.options );
 }
 
-static void vg_settings_gui(void)
+static void vg_settings_gui( ui_context *ctx )
 {
    ui_rect null;
    ui_rect screen = { 0, 0, vg.window_x, vg.window_y };
    ui_rect window = { 0, 0, 1000, 700 };
    ui_rect_center( screen, window );
-   vg_ui.wants_mouse = 1;
+   ui_capture_mouse( ctx, 1 );
 
-   ui_fill( window, ui_colour( k_ui_bg+1 ) );
-   ui_outline( window, 1, ui_colour( k_ui_bg+7 ), 0 );
+   ui_fill( ctx, window, ui_colour( ctx, k_ui_bg+1 ) );
+   ui_outline( ctx, window, 1, ui_colour( ctx, k_ui_bg+7 ), 0 );
 
    ui_rect title, panel;
    ui_split( window, k_ui_axis_h, 28, 0, title, panel );
-   ui_fill( title, ui_colour( k_ui_bg+7 ) );
-   ui_text( title, "Settings", 1, k_ui_align_middle_center, 
-            ui_colourcont(k_ui_bg+7) );
+   ui_fill( ctx, title, ui_colour( ctx, k_ui_bg+7 ) );
+   ui_text( ctx, title, "Settings", 1, k_ui_align_middle_center, 
+            ui_colourcont(ctx, k_ui_bg+7) );
 
    ui_rect quit_button;
    ui_split( title, k_ui_axis_v, title[2]-title[3], 2, title, quit_button );
 
-   if( ui_button_text( quit_button, "X", 1 ) == k_ui_button_click )
+   if( ui_button_text( ctx, quit_button, "X", 1 ) == k_ui_button_click )
    {
       vg_settings_close();
       return;
@@ -1145,31 +1277,32 @@ static void vg_settings_gui(void)
    };
 
    static i32 page = 0;
-   ui_tabs( panel, panel, opts, vg_list_size(opts), &page );
+   ui_tabs( ctx, panel, panel, opts, VG_ARRAY_LEN(opts), &page );
 
-   if( page == 0 ){
-      vg_settings_video_gui( panel );
+   if( page == 0 )
+   {
+      vg_settings_video_gui( ctx, panel );
    }
    else if( page == 1 )
-      vg_settings_audio_gui( panel );
+      vg_settings_audio_gui( ctx, panel );
 
 #ifdef VG_GAME_SETTINGS
    else if( page == 2 )
-      vg_game_settings_gui( panel );
+      vg_game_settings_gui( ctx, panel );
 #endif
 }
 
-static int cmd_vg_settings_toggle( int argc, const char *argv[] ){
+static int cmd_vg_settings_toggle( int argc, const char *argv[] )
+{
    vg_settings_open();
    return 0;
 }
 
 /*
  * Graphic cards will check these to force it to use the GPU.
- *   TODO: explicit declexport. since -flto strips these symbols in release.
  */
-u32 NvOptimusEnablement = 0x00000001;
-int AmdPowerXpressRequestHighPerformance = 1;
+__attribute__((used)) u32 NvOptimusEnablement = 0x00000001;
+__attribute__((used)) int AmdPowerXpressRequestHighPerformance = 1;
 
 #include "vg_async.c"
 #include "vg_audio.c"
@@ -1180,7 +1313,6 @@ int AmdPowerXpressRequestHighPerformance = 1;
 #include "vg_camera.c"
 #include "vg_lines.c"
 #include "vg_console.c"
-#include "vg_imgui.c"
 #include "vg_input.c"
 #include "vg_io.c"
 #include "vg_loader.c"
@@ -1194,11 +1326,16 @@ int AmdPowerXpressRequestHighPerformance = 1;
 #include "vg_perlin.c"
 #include "vg_string.c"
 #include "vg_profiler.c"
+#include "vg_magi.c"
 #include "vg_rigidbody_collision.c"
 #include "vg_rigidbody_constraints.c"
 #include "vg_rigidbody.c"
 #include "vg_rigidbody_view.c"
 #include "vg_shader.c"
+#include "vg_framebuffer.c"
+#include "vg_render.c"
+#include "vg_opengl.c"
+#include "vg_mem_view.c"
 
 #ifdef VG_CUSTOM_SHADERS
  #include "shaders/impl.c"