+
+ vg_shaders_compile();
+ vg_release_thread_sync();
+}
+
+VG_STATIC void vg_preload(void);
+VG_STATIC void vg_load(void);
+VG_STATIC void vg_load_full(void)
+{
+ vg_preload();
+
+ /* internal */
+ vg_loader_highwater( vg_gamepad_init, NULL, 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 );
+
+ /* client */
+ vg_load();
+
+ vg_acquire_thread_sync();
+ vg.is_loaded = 1;
+ vg_release_thread_sync();
+}
+
+VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
+{
+ char *arg;
+ while( vg_argp( argc, argv ) )
+ {
+ if( (arg = vg_opt_arg( 'w' )) )
+ {
+ vg.window_x = atoi( arg );
+ }
+
+ if( (arg = vg_opt_arg( 'h' )) )
+ {
+ vg.window_y = atoi( arg );
+ }
+
+ if( (arg = vg_long_opt_arg( "samples" )) )
+ {
+ vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
+ }
+
+ if( vg_long_opt( "use-libc-malloc" ) )
+ {
+ vg_mem.use_libc_malloc = atoi( arg );
+ }
+
+ if( vg_long_opt( "high-performance" ) )
+ {
+ vg.quality_profile = k_quality_profile_low;
+ }
+ }
+
+ vg_alloc_quota();
+ 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_FALSE );
+ glfwWindowHint( GLFW_CONTEXT_RELEASE_BEHAVIOR, GLFW_RELEASE_BEHAVIOR_FLUSH );
+
+ glfwWindowHint( GLFW_RESIZABLE, GLFW_FALSE );
+ glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
+
+ glfwWindowHint( GLFW_SAMPLES, vg.samples );
+
+ GLFWmonitor *monitor_primary = glfwGetPrimaryMonitor();
+
+ const GLFWvidmode *mode = glfwGetVideoMode( monitor_primary );
+ glfwWindowHint( GLFW_RED_BITS, mode->redBits );
+ glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
+ glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
+
+ glfwWindowHint( GLFW_REFRESH_RATE, mode->refreshRate );
+
+ if( !vg.window_x )
+ vg.window_x = mode->width;
+
+ if( !vg.window_y )
+ vg.window_y = mode->height;
+
+ vg.refresh_rate = mode->refreshRate;
+
+ if( (vg.window = glfwCreateWindow( vg.window_x, vg.window_y,
+ window_name, monitor_primary, NULL)) )
+ {
+ glfwGetFramebufferSize( vg.window, &vg.window_x, &vg.window_y );
+ vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
+ }
+ else
+ {
+ vg_error( "GLFW Failed to initialize\n" );
+ return;
+ }
+
+ /* We need 3.1.2 for correct VSync on windows */
+ {
+ int vmaj, vmin, vrev;
+ glfwGetVersion( &vmaj, &vmin, &vrev );
+
+ if( vmaj < 3 ||
+ (vmaj == 3 && vmin < 1) ||
+ (vmaj == 3 && vmin == 1 && vrev < 2 ) )
+ {
+ vg_error( "GLFW out of date (%d.%d.%d); (3.1.2 is required)\n",
+ vmaj, vmin, vrev );
+
+ glfwTerminate();
+ return;
+ }
+
+ vg_success( "GLFW Version %d.%d.%d\n", vmaj, vmin, vrev );
+ }
+
+ glfwMakeContextCurrent( vg.window );
+ glfwSwapInterval( 1 );
+
+ 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 );
+
+ glfwSetCharCallback( vg.window, console_proc_wchar );
+ glfwSetKeyCallback( vg.window, console_proc_key );
+ glfwSetInputMode( vg.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED );
+
+ if( glfwRawMouseMotionSupported() )
+ glfwSetInputMode( vg.window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE );
+
+ if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) )
+ {
+ 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 );
+
+ /* init systems
+ * -----------------------------------------------------------------------*/
+ ui_init_context();
+ vg_loader_init();
+
+ vg_mutex_init( &vg.mux_engine_status );
+ vg.engine_status = k_engine_status_running;
+
+ vg_opengl_sync_init();
+ vg_loader_start();
+
+ vg.accumulator = 0.75f * (1.0f/60.0f);
+
+ int loaded = 0;
+ while(1)
+ {
+ if( glfwWindowShouldClose( vg.window ) )
+ break;
+
+ v2_zero( vg.mouse_wheel );
+ v2_zero( vg.mouse_delta );
+
+ glfwPollEvents();
+
+ vg.time_real_last = vg.time_real;
+ vg.time_real = glfwGetTime();
+ vg.frame_delta = vg.time_real-vg.time_real_last;
+
+ /* scaled time */
+ vg.time_delta = vg.frame_delta * vg.time_rate;
+ vg.time += vg.time_delta;
+
+ if( vg.is_loaded )
+ {
+ if( !loaded )
+ {
+ vg_start();
+ loaded = 1;
+ }
+ }
+ else
+ {
+ vg_loader_render();
+ }
+
+ /*
+ * Game logic
+ * -------------------------------------------------------
+ */
+ vg_profile_begin( &vg_prof_update );
+ vg_update_inputs();
+
+ vg.engine_stage = k_engine_stage_update;
+ vg_update( loaded );
+
+ /* Fixed update loop */
+ vg.engine_stage = k_engine_stage_update_fixed;
+ vg.accumulator += vg.time_delta;
+
+ vg.fixed_iterations = 0;
+ vg_lines.allow_input = 1;
+ while( vg.accumulator >= (VG_TIMESTEP_FIXED-0.00125) )
+ {
+ vg_update_fixed( loaded );
+ vg_lines.allow_input = 0;
+
+ vg.accumulator -= VG_TIMESTEP_FIXED;
+ vg.accumulator = VG_MAX( 0.0, vg.accumulator );
+
+ vg.fixed_iterations ++;
+ if( vg.fixed_iterations == 8 )
+ {
+ break;
+ }
+ }
+ vg_lines.allow_input = 1;
+
+ /*
+ * Rendering
+ * ---------------------------------------------
+ */
+ vg.engine_stage = k_engine_stage_update;
+ vg_update_post( loaded );
+ vg_profile_end( &vg_prof_update );
+
+ vg_profile_begin( &vg_prof_render );
+
+ if( loaded )
+ {
+ /* 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 );
+
+ /* TODO */
+ ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
+
+ vg_profile_drawn(
+ (struct vg_profile *[]){&vg_prof_update,&vg_prof_render}, 2,
+ (1.0f/(float)vg.refresh_rate)*1000.0f,
+ (ui_rect){ 4, 4, 250, 0 }, 0
+ );
+
+ if( vg_profiler )
+ {
+
+ char perf[128];
+
+ snprintf( perf, 127,
+ "x: %d y: %d\n"
+ "refresh: %.1f (%.1fms)\n"
+ "samples: %d\n"
+ "iterations: %d (acc: %.3fms%%)\n",
+ vg.window_x, vg.window_y,
+ vg.refresh_rate, (1.0f/vg.refresh_rate)*1000.0f,
+ vg.samples,
+ vg.fixed_iterations,
+ (vg.accumulator/VG_TIMESTEP_FIXED)*100.0f );
+
+ ui_text( (ui_rect){258, 4+24+12,0,0},perf, 1,0);
+ }
+
+ audio_debug_ui( vg.pv );
+ vg_ui();
+ vg_console_draw();
+
+ ui_resolve();
+ ui_draw( NULL );
+ }
+ }
+
+ vg_profile_end( &vg_prof_render );
+
+ glfwSwapBuffers( vg.window );
+ vg_run_synced_content();
+ }
+
+ vg_console_write_persistent();
+
+ vg_mutex_lock( &vg.mux_engine_status );
+ vg.engine_status = k_engine_status_none;
+ vg_mutex_unlock( &vg.mux_engine_status );
+
+ vg_loader_free();
+
+ vg_success( "If you see this it means everything went.. \"well\".....\n" );
+ glfwTerminate();
+}
+
+/*
+ * 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?
+ */
+
+#ifndef _WIN32
+
+ void *array[20];
+ char **strings;
+ int size, i;
+
+ size = backtrace( array, 20 );
+ strings = backtrace_symbols( array, size );
+
+ if( strings != NULL )
+ {
+ vg_error( "---------------- gnu backtrace -------------\n" );
+
+ for( int i=0; i<size; i++ )
+ vg_info( "%s\n", strings[i] );
+
+ vg_error( "---------------- gnu backtrace -------------\n" );
+ }
+
+ free( strings );
+