-/* Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved */
+/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
-#include <stdio.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <math.h>
+/*
+ * Memory model:
+ * [global (.data)] [temp-stack] [system-stack] [game-heap]
+ *
+ * 1. Program starts: .data memory is loaded
+ * 2. System initialization:
+ * 2a. the large heap buffer is allocated
+ * 2b. each engine system is initialized in order, using some of the
+ * system stack
+ * 2c. game systems are also put into here
+ */
-#include "glad/glad.h"
-#include "glfw/glfw3.h"
-#define STB_DS_IMPLEMENTATION
-#include "stb/stb_ds.h"
+#ifndef VG_HEADER_H
+#define VG_HEADER_H
-#define QOI_IMPLEMENTATION
-#include "phoboslab/qoi.h"
+#include "vg_platform.h"
+#include "vg_mem.h"
-#include "vg/vg_platform.h"
+#ifndef _WIN32
+#include <execinfo.h>
+#endif
-void vg_register_exit( void( *funcptr )(void), const char *name );
-void vg_exiterr( const char *strErr );
-#include "vg/vg_m.h"
-#include "vg/vg_io.h"
-#include "vg/vg_gldiag.h"
+#if defined(VG_SERVER) || defined(VG_TOOLS)
+ #define VG_NON_CLIENT
+#endif
-#ifndef VG_TOOLS
+#ifndef VG_SERVER
+#include "../../dep/glad/glad.h"
-/* Engine globals */
-GLFWwindow* vg_window;
+#define GLFW_INCLUDE_GLCOREARB
-#ifdef VG_3D
- m4x4f vg_pv;
-#else
- m3x3f vg_pv;
+#ifdef _WIN32
+ #define GLFW_DLL
#endif
-#ifdef VG_CAPTURE_MODE
-int vg_window_x = 1920;
-int vg_window_y = 1080;
-#else
-int vg_window_x = 1366;
-int vg_window_y = 768;
+#include "../../dep/glfw/glfw3.h"
#endif
-v2f vg_mouse;
-v2f vg_mouse_wheel;
-v3f vg_mouse_ws;
+#include "vg_stdint.h"
-double vg_time,
- vg_time_last,
- vg_time_delta;
+void vg_register_exit( void( *funcptr )(void), const char *name );
-#include "vg/vg_audio.h"
-#include "vg/vg_shader.h"
-#include "vg/vg_lines.h"
-#include "vg/vg_tex.h"
-#include "vg/vg_input.h"
-#include "vg/vg_ui.h"
-#include "vg/vg_console.h"
-#include "vg/vg_debug.h"
+#include "vg_m.h"
+#include "vg_io.h"
+#include "vg_log.h"
#ifdef VG_STEAM
-#include "vg/vg_steamworks.h"
+//#include "vg_steamworks.h"
+#include "vg_steam.h"
#endif
-#ifndef VG_RELEASE
-void vg_checkgl( const char *src_info )
+#ifndef VG_NON_CLIENT
+
+struct vg
{
- GLenum err;
- while( (err = glGetError()) != GL_NO_ERROR )
+ /* Engine sync */
+ GLFWwindow* window;
+
+ vg_mutex mux_context;
+ vg_semaphore sem_allow_exec,
+ sem_exec_finished,
+ sem_loader,
+ sem_fatal;
+ int exec_context;
+
+ vg_mutex mux_engine_status;
+ enum engine_status
{
- vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
+ k_engine_status_none,
+ k_engine_status_running,
+ k_engine_status_crashed
+ }
+ engine_status;
+ const char *str_const_engine_err;
+ int is_loaded;
+
+ /* Window information */
+ int window_x,
+ window_y,
+ samples;
+ float refresh_rate;
+
+ double mouse_pos[2];
+ v2f
+ mouse_delta,
+ mouse_wheel;
+
+ /* Runtime */
+ double time,
+ time_delta,
+ frame_delta,
+ time_real,
+ time_real_last,
+ time_rate,
+ accumulator;
+
+ int fixed_iterations;
+
+ enum engine_stage
+ {
+ k_engine_stage_none,
+ k_engine_stage_update,
+ k_engine_stage_update_fixed,
+ k_engine_stage_rendering,
+ k_engine_stage_ui
+ }
+ engine_stage;
+
+ /* graphics */
+ m4x4f pv;
+ enum quality_profile
+ {
+ k_quality_profile_high = 0,
+ k_quality_profile_low = 1,
}
+ quality_profile;
+
+ /* Gamepad */
+ GLFWgamepadstate gamepad;
+ int gamepad_ready;
+ const char *gamepad_name;
+ int gamepad_id;
+ int gamepad_use_trackpad_look;
}
+VG_STATIC vg = { .time_rate = 1.0 };
- #define VG_STRINGIT( X ) #X
- #define VG_CHECK_GL() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
+struct vg_thread_info
+{
+ enum vg_thread_purpose
+ {
+ k_thread_purpose_nothing,
+ k_thread_purpose_main,
+ k_thread_purpose_loader
+ }
+ purpose;
+
+ int gl_context_level;
+};
+
+static VG_THREAD_LOCAL struct vg_thread_info vg_thread_info;
+
+
+//#define VG_SYNC_DEBUG
+
+#ifdef VG_SYNC_DEBUG
+ #define VG_SYNC_LOG(STR,...) vg_info(STR,vg_thread_info.purpose,##__VA_ARGS__)
#else
- #define VG_CHECK_GL()
+ #define VG_SYNC_LOG(...)
#endif
+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 );
+ }
+}
-#define VG_GAMELOOP
-
-void( *vg_on_exit[16] )(void);
-u32 vg_exit_count = 0;
-void vg_register_exit( void( *funcptr )(void), const char *name )
+VG_STATIC void vg_ensure_engine_running(void)
{
- vg_info( "exit registered: (%u)'%s'\n", vg_exit_count, name );
- vg_on_exit[ vg_exit_count ++ ] = funcptr;
+ /* Check if the engine is no longer running */
+ vg_mutex_lock( &vg.mux_engine_status );
+ if( vg.engine_status != k_engine_status_running )
+ {
+ VG_SYNC_LOG( "[%d] Engine is no longer running\n");
+ vg_mutex_unlock( &vg.mux_engine_status );
+
+ /* Safe to disregard loader thread from this point on, elswhere */
+ if( vg_thread_info.purpose == k_thread_purpose_loader )
+ {
+ vg_semaphore_post( &vg.sem_loader );
+ }
+
+ VG_SYNC_LOG( "[%d] about to kill\n");
+ vg_thread_exit();
+ }
+ vg_mutex_unlock( &vg.mux_engine_status );
}
-void vg_exit(void)
+/*
+ * 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)
{
- for( int i = vg_exit_count-1; i >= 0; i -- )
+ /* We dont want to do anything if this is the main thread */
+ if( vg_thread_info.purpose == k_thread_purpose_main )
+ return;
+
+ assert( vg_thread_info.purpose == k_thread_purpose_loader );
+
+ vg_ensure_engine_running();
+
+ /* Check if thread already has the context */
+ if( vg_thread_info.gl_context_level )
{
- vg_info( "engine_exit[%d]()\n", i );
- vg_on_exit[i]();
+ vg_thread_info.gl_context_level ++;
+ VG_SYNC_LOG( "[%d] We already have sync here\n" );
+ return;
}
+
+ vg_mutex_lock( &vg.mux_context );
+ VG_SYNC_LOG( "[%d] Signal to sync.\n" );
+ vg.exec_context = 1;
+ vg_mutex_unlock( &vg.mux_context );
- vg_info( "done\n" );
+ /* wait until told we can go */
+ VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n" );
+ vg_semaphore_wait( &vg.sem_allow_exec );
+ glfwMakeContextCurrent( vg.window );
+
+ /* context now valid to work in while we hold up main thread */
+ VG_SYNC_LOG( "[%d] Context acquired.\n" );
+ vg_thread_info.gl_context_level ++;
}
-void vg_exiterr( const char *strErr )
+/*
+ * 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)
{
- vg_error( "Engine Fatal: %s\n", strErr );
- vg_exit();
- exit(0);
+ if( vg_thread_info.purpose == k_thread_purpose_main )
+ return;
+
+ assert( vg_thread_info.purpose == k_thread_purpose_loader );
+
+ /* signal that we are done */
+ vg_thread_info.gl_context_level --;
+
+ if( !vg_thread_info.gl_context_level )
+ {
+ VG_SYNC_LOG( "[%d] Releasing context.\n" );
+ glfwMakeContextCurrent( NULL );
+ vg_semaphore_post( &vg.sem_exec_finished );
+ }
}
-void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
+VG_STATIC void vg_run_synced_content(void)
{
- vg_mouse[0] = xpos;
- vg_mouse[1] = ypos;
+ assert( vg_thread_info.purpose == k_thread_purpose_main );
+
+ vg_mutex_lock( &vg.mux_context );
+
+ if( vg.exec_context != 0 )
+ {
+ VG_SYNC_LOG( "[%d] Allowing content (%d).\n", vg.exec_context );
+
+ /* allow operations to go */
+ vg_thread_info.gl_context_level = 0;
+ glfwMakeContextCurrent( NULL );
+ vg_semaphore_post( &vg.sem_allow_exec );
+
+ /* wait for operations to complete */
+ VG_SYNC_LOG( "[%d] Waiting for content (%d).\n", vg.exec_context );
+ vg_semaphore_wait( &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" );
+ vg.exec_context = 0;
+ glfwMakeContextCurrent( vg.window );
+ vg_thread_info.gl_context_level = 1;
+ }
+
+ vg_mutex_unlock( &vg.mux_context );
}
-void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
+VG_STATIC void vg_opengl_sync_init(void)
{
- vg_mouse_wheel[0] += xoffset;
- vg_mouse_wheel[1] += yoffset;
+ vg_semaphore_init( &vg.sem_allow_exec, 0 );
+ vg_semaphore_init( &vg.sem_exec_finished, 0 );
+ vg_semaphore_init( &vg.sem_loader, 1 );
+ vg_semaphore_init( &vg.sem_fatal, 1 );
+ vg_mutex_init( &vg.mux_context );
+
+ vg_set_thread_name( "[vg] Main" );
+ vg_thread_info.purpose = k_thread_purpose_main;
+ vg_thread_info.gl_context_level = 1;
}
+VG_STATIC void 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_console.h"
+#include "vg_profiler.h"
+#include "vg_audio.h"
+#include "vg_shader.h"
+#include "vg_tex.h"
+#include "vg_input.h"
+#include "vg_ui.h"
+#include "vg_lines.h"
+#include "vg_loader.h"
+#include "vg_opt.h"
+
+/* Diagnostic */
+VG_STATIC struct vg_profile vg_prof_update = {.name="update()"},
+ vg_prof_render = {.name="render()"};
-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;
+#define VG_GAMELOOP
+VG_STATIC void vg_register(void) VG_GAMELOOP;
+VG_STATIC void vg_start(void) VG_GAMELOOP;
+
+VG_STATIC void vg_update(int loaded) VG_GAMELOOP;
+VG_STATIC void vg_update_fixed(int loaded) VG_GAMELOOP;
+VG_STATIC void vg_update_post(int loaded) VG_GAMELOOP;
+
+VG_STATIC void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
+VG_STATIC void vg_render(void) VG_GAMELOOP;
+VG_STATIC void vg_ui(void) VG_GAMELOOP;
+
+VG_STATIC void 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;
+ }
+
+ if( fail )
+ vg_fatal_exit_loop( "OpenGL Error" );
+}
+
+void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
+{
+ vg.mouse_delta[0] += xpos - vg.mouse_pos[0];
+ vg.mouse_delta[1] += ypos - vg.mouse_pos[1];
+
+ vg.mouse_pos[0] = xpos;
+ vg.mouse_pos[1] = ypos;
+}
+
+void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
+{
+ vg.mouse_wheel[0] += xoffset;
+ vg.mouse_wheel[1] += yoffset;
+}
void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
{
- vg_window_x = w;
- vg_window_y = h;
+ if( !w || !h )
+ {
+ vg_warn( "Got a invalid framebuffer size: %dx%d... ignoring\n", w, h );
+ return;
+ }
+
+ 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 )
+VG_STATIC void vg_bake_shaders(void)
{
-#ifdef VG_STEAM
- if( !sw_init() )
- return;
+ vg_acquire_thread_sync();
+
+#if 0
+ vg_function_push( (struct vg_cmd)
+ {
+ .name = "shaders",
+ .function = vg_shaders_live_recompile
+ });
#endif
+
+ 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_TRUE );
+ //glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_FALSE );
+ glfwWindowHint( GLFW_CONTEXT_RELEASE_BEHAVIOR, GLFW_RELEASE_BEHAVIOR_FLUSH );
-#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);
-#endif
+ glfwWindowHint( GLFW_SAMPLES, vg.samples );
GLFWmonitor *monitor_primary = glfwGetPrimaryMonitor();
glfwWindowHint( GLFW_RED_BITS, mode->redBits );
glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
-
- int refresh_rate = mode->refreshRate;
- if( refresh_rate < 28 || refresh_rate >= 144 )
- refresh_rate = 60;
+ glfwWindowHint( GLFW_REFRESH_RATE, mode->refreshRate );
+
+ if( !vg.window_x )
+ vg.window_x = mode->width;
- glfwWindowHint( GLFW_REFRESH_RATE, refresh_rate );
+ if( !vg.window_y )
+ vg.window_y = mode->height;
- if( !(vg_window = glfwCreateWindow( vg_window_x, vg_window_y,
- window_name, NULL, NULL)) )
- vg_exiterr( "GLFW Failed to initialize" );
+ 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_register_exit( &glfwTerminate, "glfwTerminate" );
+ {
+ vg_error( "GLFW Failed to initialize\n" );
+ return;
+ }
- glfwMakeContextCurrent( vg_window );
+ /* 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 );
+ 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 );
-#if 0
- glfwSetInputMode(vg_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
-#endif
+ 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_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();
-
- for( int id = 0; id <= GLFW_JOYSTICK_LAST; id ++ )
+
+ /* 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( glfwJoystickIsGamepad( id ) )
- {
- vg_gamepad_name = glfwGetGamepadName( id );
- vg_success( "Gamepad with mapping registered: %s\n", vg_gamepad_name );
-
- vg_gamepad_ready = 1;
- vg_gamepad_id = id;
-
+ if( glfwWindowShouldClose( vg.window ) )
break;
- }
- }
-
- 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( vg_shaders_compile() )
- {
- vg_start();
-
- vg_console_init();
- vg_register_exit( &vg_console_free, "Console" );
-
- vg_audio_init();
- vg_register_exit( &vg_audio_free, "vg_audio_free" );
+
+ 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;
- vg_debugtools_setup();
+ /* 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();
+ }
+
/*
- * Main gameloop
+ * Game logic
+ * -------------------------------------------------------
*/
- while( !glfwWindowShouldClose( vg_window ) )
+ 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) )
{
- v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
+ vg_update_fixed( loaded );
+ vg_lines.allow_input = 0;
- 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.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();
-
- vg_lines_drawall((float*)vg_pv);
-
+
+ /* ui */
+ vg.engine_stage = k_engine_stage_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" ) );
+ 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();
- vg_debugtools_draw();
- ui_resolve( &ui_global_ctx );
- ui_draw( &ui_global_ctx, NULL );
+ ui_resolve();
+ ui_draw( NULL );
}
-
- glfwSwapBuffers( vg_window );
- VG_CHECK_GL();
}
+
+ vg_profile_end( &vg_prof_render );
+
+ glfwSwapBuffers( vg.window );
+ vg_run_synced_content();
}
-
- vg_exit();
+
+ 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();
}
-#ifndef VG_3D
-void vg_projection_update(void)
+/*
+ * 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 )
{
- /*
- * Reproject screenspace mouse into world
+ /*
+ * https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
+ * thanks gnu <3
+ *
+ * TODO: this on windows?
*/
- 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 );
-}
+#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 );
+
#endif
+ vg_error( "Fatal error: %s\n", error );
+ assert( vg_semaphore_trywait( &vg.sem_fatal ) );
+
+ vg_mutex_lock( &vg.mux_engine_status );
+
+ if( vg.engine_status == k_engine_status_none )
+ {
+ vg_mutex_unlock( &vg.mux_engine_status );
+
+ /* TODO: Correct shutdown before other systems */
+ exit(0);
+ }
+ else
+ {
+ vg_mutex_unlock( &vg.mux_engine_status );
+
+ /*
+ * 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();
+
+ vg_mutex_lock( &vg.mux_engine_status );
+ vg.engine_status = k_engine_status_crashed;
+ vg.str_const_engine_err = error;
+ vg_mutex_unlock( &vg.mux_engine_status );
+
+ /*
+ * 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_info.purpose == k_thread_purpose_main )
+ {
+ vg_semaphore_wait( &vg.sem_loader );
+ }
+ vg_audio_free(NULL);
+
+ while(1)
+ {
+ if( glfwWindowShouldClose( vg.window ) )
+ break;
+
+ if( glfwGetKey( vg.window, GLFW_KEY_ESCAPE ) )
+ break;
+
+ glfwPollEvents();
+
+ 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(glfwGetTime())*0.1f, 0.0f, 0.0f,1.0f );
+ glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+ glViewport( 0,0, vg.window_x, vg.window_y );
+
+ vg_render_log();
+
+ glfwSwapBuffers( vg.window );
+ }
+
+ /* Can now shutdown and EXIT */
+ vg_loader_free();
+ glfwTerminate();
+ exit(0);
+ }
+}
+
+#else
+
+VG_STATIC void vg_fatal_exit_loop( const char *error )
+{
+ vg_error( "Fatal error: %s\n", error );
+ exit(0);
+}
+
#endif
/*
*/
u32 NvOptimusEnablement = 0x00000001;
int AmdPowerXpressRequestHighPerformance = 1;
+
+#endif