medium sized dollop
[vg.git] / src / vg / vg.h
index 25771ff8d142fc9ff7b180a0048172691feb78d3..b076091d1c627ba252d32ccf81d38165b94895f9 100644 (file)
@@ -1,7 +1,4 @@
-/* Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved */
-
-static void vg_exiterr( const char *err );
-static void vg_exit(void);
+/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
 
 #ifndef VG_HEADER_H
 #define VG_HEADER_H
@@ -36,6 +33,7 @@ void vg_register_exit( void( *funcptr )(void), const char *name );
 
 #include "vg_m.h"
 #include "vg_io.h"
+#include "vg_log.h"
 
 #ifdef VG_STEAM
 //#include "vg_steamworks.h"
@@ -48,14 +46,7 @@ void vg_register_exit( void( *funcptr )(void), const char *name );
 
 #ifndef VG_NON_CLIENT
 
-/* Engine globals */
-GLFWwindow* vg_window;
-
-#ifdef VG_3D
- m4x4f vg_pv;
-#else
- m3x3f vg_pv;
-#endif
+m4x4f vg_pv;
 
 #ifdef VG_CAPTURE_MODE
 int vg_window_x = 1920;
@@ -73,61 +64,171 @@ double vg_time,
        vg_time_last,
        vg_time_delta;
 
-#include "vg_audio.h"
-#include "vg_shader.h"
-#include "vg_tex.h"
-#include "vg_input.h"
-#include "vg_ui.h"
-#include "vg_console.h"
-#include "vg_lines.h"
-#include "vg_debug.h"
 
-#ifndef VG_RELEASE
-void vg_checkgl( const char *src_info )
+struct vg
 {
-   GLenum err;
-   while( (err = glGetError()) != GL_NO_ERROR )
-   {
-      vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
-   }
+   /* Engine sync */
+   GLFWwindow* window;
+
+   vg_mutex     mux_context;
+   vg_semaphore sem_allow_exec,
+                sem_exec_finished;
+   int exec_context;
+
+   vg_mutex     mux_engine_status;
+   int engine_running;
+
+
+   /* Gamepad */
+   GLFWgamepadstate  gamepad;
+   int                               gamepad_ready;
+   const char       *gamepad_name;
+   int                               gamepad_id;
 }
+static vg;
+
 
- #define VG_STRINGIT( X ) #X
- #define VG_CHECK_GL() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
+//#define VG_SYNC_DEBUG
+
+#ifdef VG_SYNC_DEBUG
+  #define VG_SYNC_LOG(...) vg_info(__VA_ARGS__)
 #else
#define VG_CHECK_GL()
 #define VG_SYNC_LOG(...)
 #endif
 
+/*
+ * Sync execution so that the OpenGL context is switched onto this thread.
+ * Anything after this call will be in a valid context.
+ */
+static int vg_acquire_thread_sync( int id )
+{
+   if( id == 0 )
+   {
+      /* no action */
+      return 1;
+   }
+   else
+   {
+      /* Check if the engine is no longer running */
+      vg_mutex_lock( &vg.mux_engine_status );
+      if( !vg.engine_running )
+      {
+         VG_SYNC_LOG( "[%d] Engine is no longer running\n", id );
+         vg_mutex_unlock( &vg.mux_engine_status );
+         return 0;
+      }
+      vg_mutex_unlock( &vg.mux_engine_status );
 
-#define VG_GAMELOOP
 
-void( *vg_on_exit[16] )(void);
-u32 vg_exit_count = 0;
+      vg_mutex_lock( &vg.mux_context );
+      VG_SYNC_LOG( "[%d] Signal to sync.\n", id );
+      vg.exec_context = id;
+      vg_mutex_unlock( &vg.mux_context );
+      
+      /* wait until told we can go */
+      VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n", id );
+      vg_semaphore_wait( &vg.sem_allow_exec );
+      glfwMakeContextCurrent( vg.window );
 
-void vg_register_exit( void( *funcptr )(void), const char *name )
-{
-   vg_info( "exit registered: (%u)'%s'\n", vg_exit_count, name );
-   vg_on_exit[ vg_exit_count ++ ] = funcptr;
+      /* context now valid to work in while we hold up main thread */
+      VG_SYNC_LOG( "[%d] Context acquired.\n", id );
+
+      return 1;
+   }
 }
 
-static void vg_exit(void)
+/*
+ * Signify that we are done with the OpenGL context in this thread.
+ * Anything after this call will be in an undefined context.
+ */
+static void vg_release_thread_sync( int id )
 {
-   for( int i = vg_exit_count-1; i >= 0; i -- )
+   if( id == 0 )
+   {
+      vg_mutex_lock( &vg.mux_context );
+
+      if( vg.exec_context != 0 )
+      {
+         VG_SYNC_LOG( "[%d] Allowing content (%d).\n", id, vg.exec_context );
+
+         /* allow operations to go */
+         glfwMakeContextCurrent( NULL );
+         vg_semaphore_post( &vg.sem_allow_exec );
+
+         /* wait for operations to complete */
+         VG_SYNC_LOG( "[%d] Waiting for content (%d).\n", id, vg.exec_context );
+         vg_semaphore_wait( &vg.sem_exec_finished );
+         
+         /* re-engage main thread */
+         VG_SYNC_LOG( "[%d] Re-engaging.\n", id );
+         vg.exec_context = 0;
+         glfwMakeContextCurrent( vg.window );
+      }
+
+      vg_mutex_unlock( &vg.mux_context );
+   }
+   else
    {
-      vg_info( "engine_exit[%d]()\n", i );
-      vg_on_exit[i]();
+      /* signal that we are done */
+      VG_SYNC_LOG( "[%d] Releasing context.\n", id );
+      glfwMakeContextCurrent( NULL );
+      vg_semaphore_post( &vg.sem_exec_finished );
    }
-   
-   vg_info( "done\n" );
-   exit(0);
 }
 
-static void vg_exiterr( const char *err )
+static void vg_opengl_sync_init(void)
 {
-   vg_error( "Engine Fatal: %s\n", err );
-   vg_exit();
+   vg_semaphore_init( &vg.sem_allow_exec, 0 );
+   vg_semaphore_init( &vg.sem_exec_finished, 0 );
+   vg_mutex_init( &vg.mux_context );
 }
 
+static void vg_opengl_sync_free(void)
+{
+   vg_semaphore_free( &vg.sem_allow_exec );
+   vg_semaphore_free( &vg.sem_exec_finished );
+   vg_mutex_free( &vg.mux_context );
+}
+
+
+int vg_checkgl( const char *src_info );
+#define VG_STRINGIT( X ) #X
+#define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
+
+#include "vg_audio.h"
+#include "vg_shader.h"
+#include "vg_tex.h"
+#include "vg_input.h"
+#include "vg_ui.h"
+#include "vg_console.h"
+#include "vg_lines.h"
+#include "vg_debug.h"
+#include "vg_loader.h"
+
+#define VG_GAMELOOP
+static void vg_register(void) VG_GAMELOOP;
+static void vg_start(void) VG_GAMELOOP;
+static void vg_update(int loaded) VG_GAMELOOP;
+static void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
+static void vg_render(void) VG_GAMELOOP;
+static void vg_ui(void) VG_GAMELOOP;
+
+int vg_checkgl( const char *src_info )
+{
+   int fail = 0;
+
+   GLenum err;
+   while( (err = glGetError()) != GL_NO_ERROR )
+   {
+      vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
+      fail = 1;
+   }
+
+   return fail;
+}
+
+
+
 void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
 {
    vg_mouse[0] = xpos;
@@ -140,44 +241,71 @@ void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
    vg_mouse_wheel[1] += yoffset;
 }
 
-
-static void vg_register(void) VG_GAMELOOP;
-static void vg_start(void) VG_GAMELOOP;
-static void vg_update(void) VG_GAMELOOP;
-static void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
-static void vg_render(void) VG_GAMELOOP;
-static void vg_ui(void) VG_GAMELOOP;
-static void vg_free(void) VG_GAMELOOP;
-
 void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
 {
    vg_window_x = w;
    vg_window_y = h;
 
-#ifdef VG_FRAMEBUFFER_RESIZE
    vg_framebuffer_resize(w,h);
-#endif
 }
 
-static void vg_init( int argc, char *argv[], const char *window_name )
+static int vg_bake_shaders(void)
 {
-#ifdef VG_STEAM
-   if( !sw_init() )
-      return;
-#endif
-   
+   if( vg_acquire_thread_sync(1) )
+   {
+      if( !vg_shaders_recompile() )
+      {
+         vg_shaders_free(NULL);
+         vg_release_thread_sync(1);
+         return 0;
+      }
+      else
+      {
+         vg_release_thread_sync(1);
+
+         if( !vg_loader_highwater( vg_shaders_free, NULL ) )         return 0;
+         else return 1;
+      }
+   }
+
+   return 0;
+}
+
+int vg_preload(void);
+int vg_load(void);
+static int vg_load_full(void)
+{
+   if( !vg_preload() )                                               return 0;
+
+   /* internal */
+   if( !vg_gamepad_init() )                                          return 0;
+   if( !vg_loader_highwater( NULL, NULL ) )                          return 0;
+
+   if( !vg_lines_init() )                                            return 0;
+   if( !vg_loader_highwater( vg_lines_free, NULL ) )                 return 0;
+
+   if( !vg_audio_init() )                                            return 0;
+   if( !vg_loader_highwater( vg_audio_free, NULL ) )                 return 0;
+
+   /* client */
+   if( !vg_load() )                                                  return 0;
+
+   return 1;
+}
+
+static void vg_enter( int argc, char *argv[], const char *window_name )
+{
+   vg_log_init();
+   vg_console_init();
+
    glfwInit();
    glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
    glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
    glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
    glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE );
    
-#ifdef VG_CAPTURE_MODE
-   glfwWindowHint( GLFW_RESIZABLE, GLFW_FALSE );
-#else
    glfwWindowHint( GLFW_RESIZABLE, GLFW_TRUE );
-#endif
-   glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
+   glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
    
 #if 0
    glfwWindowHint(GLFW_SAMPLES,4);
@@ -190,130 +318,151 @@ static void vg_init( int argc, char *argv[], const char *window_name )
    glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
    glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
    
+   /* This is set like this because of an OS issue */
    int refresh_rate = mode->refreshRate;
-
    if( refresh_rate < 28 || refresh_rate >= 144 )
       refresh_rate = 60;
-
    glfwWindowHint( GLFW_REFRESH_RATE, refresh_rate );
 
-   if( !(vg_window = glfwCreateWindow( vg_window_x, vg_window_y, 
+   if( !(vg.window = glfwCreateWindow( vg_window_x, vg_window_y, 
                                        window_name, NULL, NULL)) )
-      vg_exiterr( "GLFW Failed to initialize" );
-   else
-      vg_register_exit( &glfwTerminate, "glfwTerminate" );
+   {
+      vg_error( "GLFW Failed to initialize\n" );
+      return;
+   }
    
-   glfwMakeContextCurrent( vg_window );
+   glfwMakeContextCurrent( vg.window );
    glfwSwapInterval( 1 );
 
-   glfwSetWindowSizeLimits( vg_window, 800, 600, GLFW_DONT_CARE,GLFW_DONT_CARE);
-   glfwSetFramebufferSizeCallback( vg_window, vg_framebuffer_resize_callback );
+   glfwSetWindowSizeLimits( vg.window, 800, 600, GLFW_DONT_CARE,GLFW_DONT_CARE);
+   glfwSetFramebufferSizeCallback( vg.window, vg_framebuffer_resize_callback );
 
-   glfwSetCursorPosCallback( vg_window, vg_mouse_callback );
-   glfwSetScrollCallback( vg_window, vg_scroll_callback );
+   glfwSetCursorPosCallback( vg.window, vg_mouse_callback );
+   glfwSetScrollCallback( vg.window, vg_scroll_callback );
    
-   glfwSetCharCallback( vg_window, console_proc_wchar );
-   glfwSetKeyCallback( vg_window, console_proc_key );
+   glfwSetCharCallback( vg.window, console_proc_wchar );
+   glfwSetKeyCallback( vg.window, console_proc_key );
 #if 0
    glfwSetInputMode(vg_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
 #endif
 
    if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) ) 
-      vg_exiterr( "Glad failed to initialize" );
+   {
+      vg_error( "Glad Failed to initialize\n" );
+      glfwTerminate();
+      return;
+   }
 
    const unsigned char* glver = glGetString( GL_VERSION );
    vg_success( "Load setup complete, OpenGL version: %s\n", glver );
-   
    vg_run_gfx_diagnostics();
-   
-   vg_gamepad_init();
-   vg_lines_init();
-   vg_register_exit( &vg_lines_free, "vg_lines_free" );
-   ui_default_init();
-   vg_register_exit( &ui_default_free, "UI" );
-      
-   vg_register();
-   vg_register_exit( &vg_free, "vg_free" );
 
+   if( !ui_default_init() )
+      goto il_exit_ui;
 
-   vg_shaders_recompile(0,NULL);
-   vg_register_exit( &vg_shaders_free, "vg_shaders_free" );
-       vg_function_push( (struct vg_cmd){
-               .name = "shaders",
-               .function = vg_shaders_recompile
-       });
+   if( !vg_loader_init() )
+      goto il_exit_loader;
 
-   if( !vg_audio_init() )
-      vg_exit();
-   vg_register_exit( &vg_audio_free, "vg_audio_free" );
+   vg_mutex_init( &vg.mux_engine_status );
+   vg.engine_running = 1;
 
-   vg_start();
+   vg_opengl_sync_init();
+   vg_loader_start();
 
-   vg_console_init();
-   vg_register_exit( &vg_console_free, "Console" );
-   
-   /* 
-    * Main gameloop
-    */
-   while( !glfwWindowShouldClose( vg_window ) )
+   int loaded = 0;
+
+   while(1)
    {
-      v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
+      vg_acquire_thread_sync( 0 );
 
+      if( glfwWindowShouldClose( vg.window ) )
+      {
+         break;
+      }
+
+      v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
       glfwPollEvents();
-      
-      #ifdef VG_STEAM
-      sw_event_loop();
-      #endif
-      
+
       vg_time_last = vg_time;
       vg_time = glfwGetTime();
       vg_time_delta = vg_minf( vg_time - vg_time_last, 0.1f );
       
-      vg_update_inputs();
-      vg_update();
-      vg_render();
-      
-      vg_lines_drawall((float*)vg_pv);
-      
+      enum loader_status load_status = vg_loader_status();
+
+      if( load_status == k_loader_status_complete )
       {
-         ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
-         ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1], 
-               vg_get_button_state( "primary" ) );
+         glClearColor( 0.0f,sinf(vg_time*20.0)*0.5f+0.5f,0.0f,1.0f );
+         glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
          
-         audio_debug_ui( vg_pv );
-         vg_ui();
-         vg_console_draw();
-         
-         ui_resolve( &ui_global_ctx );
-         ui_draw( &ui_global_ctx, NULL );
+         if( !loaded )
+         {
+            vg_start();
+            loaded = 1;
+         }
       }
-      
-      glfwSwapBuffers( vg_window );
-      VG_CHECK_GL();
+      else
+      {
+         if( load_status == k_loader_status_fail )
+         {
+            break;
+         }
+         else
+         {
+            vg_loader_render();
+         }
+      }
+
+      vg_update_inputs();
+      vg_update( loaded );
+
+      if( loaded )
+      {
+         vg_render();
+
+         /* ui */
+         {
+            ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
+            ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1], 
+                  vg_get_button_state( "primary" ) );
+            
+            audio_debug_ui( vg_pv );
+            vg_ui();
+            vg_console_draw();
+            
+            ui_resolve( &ui_global_ctx );
+            ui_draw( &ui_global_ctx, NULL );
+         }
+      }
+
+
+
+
+      glfwSwapBuffers( vg.window );
+
+      if( VG_CHECK_GL_ERR() )
+         break;
+
+      vg_release_thread_sync( 0 );
    }
-   
-   vg_exit();
-}
 
-#ifndef VG_3D
-void vg_projection_update(void)
-{
-   /*
-    * Reproject screenspace mouse into world
-    */
+   vg_console_write_persistent();
 
-   vg_mouse_ws[0] = vg_mouse[0];
-   vg_mouse_ws[1] = vg_mouse[1];
-   vg_mouse_ws[2] = 1.0f;
-   
-   vg_mouse_ws[0] =   (2.0f * vg_mouse_ws[0]) / ((float)vg_window_x) - 1.0f;
-   vg_mouse_ws[1] = -((2.0f * vg_mouse_ws[1]) / ((float)vg_window_y) - 1.0f);
-   
-   m3x3f inverse;
-   m3x3_inv( vg_pv, inverse ); 
-   m3x3_mulv( inverse, vg_mouse_ws, vg_mouse_ws );
+   vg_mutex_lock( &vg.mux_engine_status );
+   vg.engine_running = 0;
+   vg_mutex_unlock( &vg.mux_engine_status );
+
+
+   vg_loader_free();
+   vg_opengl_sync_free();
+
+   vg_mutex_free( &vg.mux_engine_status );
+
+il_exit_loader:
+   ui_default_free();
+
+il_exit_ui:
+   glfwTerminate();
 }
-#endif
 
 #endif