memory related functions
[vg.git] / vg.h
1 /* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */
2
3 /*
4
5 .-. VG Event loop
6 | 0 |
7 | | .---------------------------------------------------------.
8 |API| | vg_enter( int argc, char *argv[], const char *window_name |
9 | | '---------------------------------------------------------'
10 | | |
11 | | v
12 |IMP| vg_launch_opt(void) <--.
13 | | | |
14 | | |'---------------'
15 | | | .-.
16 | | |'-----------------------------------| 1 |------.
17 | | | | | |
18 | | | | | v
19 | | | |IMP| vg_preload(void)
20 | | | | | |
21 | | .-----+. | | v
22 | | | | |IMP| vg_load(void)
23 | | | v '___' |
24 |IMP| | vg_framebuffer_resize(void) |
25 | | | | |
26 |IMP| | |.------------- vg_start(void) ---------------'
27 | | | |
28 | | | v
29 |IMP| | vg_update(void)
30 | | | |
31 | | | .-----+.
32 | | | | |
33 | | | | v
34 |IMP| | '- vg_update_fixed(void)
35 | | | |
36 | | | .-'
37 | | | |
38 | | | v
39 |IMP| | vg_update_post(void)
40 | | | |
41 | | | v
42 |IMP| | vg_render(void)
43 | | | |
44 | | | v
45 |IMP| | vg_ui(void)
46 | | | |
47 | | '----'
48 '___'
49
50 .-.
51 | ? |
52 | | .-------------------------------------.
53 |API| | vg_fatal_exit_loop( const char *err ) |
54 | | '-------------------------------------'
55 | | |
56 | | .------+.
57 | | | |
58 | | | v
59 |IMP| '- vg_framebuffer_resize(void)
60 '___'
61
62 */
63
64 #ifndef VG_HEADER_H
65 #define VG_HEADER_H
66
67 #include "vg_platform.h"
68 #include "vg_mem.h"
69
70 #ifndef _WIN32
71 #include <execinfo.h>
72 #endif
73
74 VG_STATIC void vg_print_backtrace(void)
75 {
76 #ifndef _WIN32
77
78 void *array[20];
79 char **strings;
80 int size, i;
81
82 size = backtrace( array, 20 );
83 strings = backtrace_symbols( array, size );
84
85 if( strings != NULL ){
86 vg_error( "---------------- gnu backtrace -------------\n" );
87
88 for( int i=0; i<size; i++ )
89 vg_info( "%s\n", strings[i] );
90
91 vg_error( "---------------- gnu backtrace -------------\n" );
92 }
93
94 free( strings );
95
96 #endif
97 }
98
99 #ifdef VG_GAME
100 #include "dep/glad/glad.h"
101 #include "submodules/SDL/include/SDL.h"
102 #include "vg_stdint.h"
103
104 void vg_register_exit( void( *funcptr )(void), const char *name );
105
106 #include "vg_m.h"
107 #include "vg_io.h"
108 #include "vg_log.h"
109 #include "vg_steam.h"
110
111 //#define VG_SYNC_DEBUG
112 #ifdef VG_SYNC_DEBUG
113 #define VG_SYNC_LOG(STR,...) \
114 vg_info(STR,SDL_GetThreadID(NULL),##__VA_ARGS__)
115 #else
116 #define VG_SYNC_LOG(...)
117 #endif
118
119 /* API */
120 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name );
121
122 /* Thread 1 */
123 VG_STATIC void vg_preload(void);
124 VG_STATIC void vg_load(void);
125
126 /* Main thread */
127 VG_STATIC void vg_launch_opt(void);
128 VG_STATIC void vg_start(void);
129
130 VG_STATIC void vg_framebuffer_resize(int w, int h);
131 VG_STATIC void vg_update(void);
132 VG_STATIC void vg_update_fixed(void);
133 VG_STATIC void vg_update_post(void);
134
135 VG_STATIC void vg_render(void);
136 VG_STATIC void vg_ui(void);
137
138 struct vg
139 {
140 /* Engine sync */
141 SDL_Window *window;
142 SDL_GLContext gl_context;
143
144 SDL_SpinLock sl_context;
145 SDL_sem *sem_allow_exec,
146 *sem_exec_finished,
147 *sem_loader;
148
149 SDL_threadID thread_id_main,
150 thread_id_loader,
151 thread_id_with_opengl_context;
152 int context_ownership_depth;
153
154 int exec_context;
155
156 enum engine_status
157 {
158 k_engine_status_none,
159 k_engine_status_running,
160 k_engine_status_crashed
161 }
162 engine_status;
163 const char *str_const_engine_err;
164 int is_loaded;
165
166 /* Window information */
167 int window_x,
168 window_y,
169 samples,
170 window_should_close;
171
172 float refresh_rate;
173
174 double mouse_pos[2];
175 v2f mouse_delta,
176 mouse_wheel;
177
178 /* Runtime */
179 double time,
180 time_delta,
181 frame_delta,
182 time_real,
183 time_real_last,
184 time_rate,
185 accumulator;
186
187 int fixed_iterations;
188
189 enum engine_stage
190 {
191 k_engine_stage_none,
192 k_engine_stage_update,
193 k_engine_stage_update_fixed,
194 k_engine_stage_rendering,
195 k_engine_stage_ui
196 }
197 engine_stage;
198
199 /* graphics */
200 m4x4f pv;
201 enum quality_profile
202 {
203 k_quality_profile_high = 0,
204 k_quality_profile_low = 1,
205 }
206 quality_profile;
207 }
208 VG_STATIC vg = { .time_rate = 1.0 };
209
210 enum vg_thread_purpose
211 {
212 k_thread_purpose_nothing,
213 k_thread_purpose_main,
214 k_thread_purpose_loader
215 };
216
217 VG_STATIC void vg_fatal_exit_loop( const char *error );
218
219 /*
220 * Checks if the engine is running
221 */
222 VG_STATIC void _vg_ensure_engine_running(void)
223 {
224 /* Check if the engine is no longer running */
225 SDL_AtomicLock( &vg.sl_context );
226 enum engine_status status = vg.engine_status;
227 SDL_AtomicUnlock( &vg.sl_context );
228
229 if( status != k_engine_status_running ){
230 while(1) {
231 VG_SYNC_LOG( "[%d] No longer running...\n");
232 SDL_Delay(1000);
233 }
234 }
235 }
236
237 VG_STATIC enum vg_thread_purpose vg_thread_purpose(void)
238 {
239 SDL_AtomicLock( &vg.sl_context );
240
241 if( vg.thread_id_main == SDL_GetThreadID(NULL) ){
242 SDL_AtomicUnlock( &vg.sl_context );
243 return k_thread_purpose_main;
244 }
245 else{
246 SDL_AtomicUnlock( &vg.sl_context );
247 return k_thread_purpose_loader;
248 }
249 }
250
251 /*
252 * Sync execution so that the OpenGL context is switched onto this thread.
253 * Anything after this call will be in a valid context.
254 */
255 VG_STATIC void vg_acquire_thread_sync(void)
256 {
257 /* We dont want to do anything if this is the main thread */
258
259 if( vg_thread_purpose() == k_thread_purpose_loader ){
260 VG_SYNC_LOG( "[%d] vg_acquire_thread_sync()\n" );
261 _vg_ensure_engine_running();
262
263 SDL_AtomicLock( &vg.sl_context );
264 if( vg.context_ownership_depth == 0 ){
265 vg.context_ownership_depth ++;
266 vg.exec_context = 1;
267 SDL_AtomicUnlock( &vg.sl_context );
268
269 /* wait until told we can go */
270 VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n" );
271 SDL_SemWait( vg.sem_allow_exec );
272
273 _vg_ensure_engine_running();
274
275 SDL_GL_MakeCurrent( vg.window, vg.gl_context );
276 VG_SYNC_LOG( "[%d] granted\n" );
277 }
278 else{
279 vg.context_ownership_depth ++;
280 VG_SYNC_LOG( "[%d] granted\n" );
281 SDL_AtomicUnlock( &vg.sl_context );
282 }
283 }
284 }
285
286 /*
287 * Signify that we are done with the OpenGL context in this thread.
288 * Anything after this call will be in an undefined context.
289 */
290 VG_STATIC void vg_release_thread_sync(void)
291 {
292 if( vg_thread_purpose() == k_thread_purpose_loader ){
293 VG_SYNC_LOG( "[%d] vg_release_thread_sync()\n" );
294
295 SDL_AtomicLock( &vg.sl_context );
296 vg.context_ownership_depth --;
297
298 if( vg.context_ownership_depth == 0 ){
299 SDL_AtomicUnlock( &vg.sl_context );
300 VG_SYNC_LOG( "[%d] Releasing context.\n" );
301 SDL_GL_MakeCurrent( NULL, NULL );
302 SDL_SemPost( vg.sem_exec_finished );
303 }
304 else
305 SDL_AtomicUnlock( &vg.sl_context );
306 }
307 }
308
309 VG_STATIC void _vg_run_synced(void)
310 {
311 SDL_AtomicLock( &vg.sl_context );
312
313 if( vg.exec_context != 0 ){
314 VG_SYNC_LOG( "[%d] _vg_run_synced() (%d).\n", vg.exec_context );
315 vg.exec_context = 0;
316 SDL_AtomicUnlock( &vg.sl_context );
317
318 /* allow operations to go */
319 SDL_GL_MakeCurrent( NULL, NULL );
320 SDL_SemPost( vg.sem_allow_exec );
321
322 /* wait for operations to complete */
323 VG_SYNC_LOG( "[%d] Waiting for content.\n" );
324 SDL_SemWait( vg.sem_exec_finished );
325
326 /* check if we killed the engine */
327 _vg_ensure_engine_running();
328
329 /* re-engage main thread */
330 VG_SYNC_LOG( "[%d] Re-engaging.\n" );
331 SDL_GL_MakeCurrent( vg.window, vg.gl_context );
332 }
333 else{
334 VG_SYNC_LOG( "[%d] Nothing to do.\n" );
335 SDL_AtomicUnlock( &vg.sl_context );
336 }
337 }
338
339 VG_STATIC void _vg_opengl_sync_init(void)
340 {
341 vg.sem_allow_exec = SDL_CreateSemaphore(0);
342 vg.sem_exec_finished = SDL_CreateSemaphore(0);
343 vg.sem_loader = SDL_CreateSemaphore(1);
344 }
345
346 VG_STATIC void vg_checkgl( const char *src_info );
347 #define VG_STRINGIT( X ) #X
348 #define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
349
350 #include "vg_console.h"
351 #include "vg_profiler.h"
352 #include "vg_audio.h"
353 #include "vg_shader.h"
354 #include "vg_tex.h"
355 #include "vg_input.h"
356 #include "vg_ui.h"
357 #include "vg_lines.h"
358 #include "vg_loader.h"
359 #include "vg_opt.h"
360
361 /* Diagnostic */
362 VG_STATIC struct vg_profile vg_prof_update = {.name="update()"},
363 vg_prof_render = {.name="render()"};
364
365 VG_STATIC void vg_checkgl( const char *src_info )
366 {
367 int fail = 0;
368
369 GLenum err;
370 while( (err = glGetError()) != GL_NO_ERROR ){
371 vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
372 fail = 1;
373 }
374
375 if( fail )
376 vg_fatal_exit_loop( "OpenGL Error" );
377 }
378
379 VG_STATIC void vg_bake_shaders(void)
380 {
381 vg_acquire_thread_sync();
382 vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL );
383
384 vg_shaders_compile();
385 vg_release_thread_sync();
386 }
387
388 VG_STATIC void _vg_load_full(void)
389 {
390 vg_preload();
391
392 /* internal */
393 vg_loader_step( vg_input_init, vg_input_free );
394 vg_loader_step( vg_lines_init, NULL );
395 vg_loader_step( vg_audio_init, vg_audio_free );
396 vg_loader_step( vg_profiler_init, NULL );
397
398 /* client */
399 vg_load();
400 }
401
402 VG_STATIC void _vg_process_events(void)
403 {
404 /* Update timers */
405 vg.time_real_last = vg.time_real;
406 vg.time_real = (double)SDL_GetTicks64() / 1000.0;
407 vg.frame_delta = vg.time_real-vg.time_real_last;
408
409 v2_zero( vg.mouse_wheel );
410 v2_zero( vg.mouse_delta );
411
412 /* SDL event loop */
413 SDL_Event event;
414 while( SDL_PollEvent( &event ) ){
415 if( event.type == SDL_KEYDOWN ){
416 console_proc_key( event.key.keysym );
417 }
418 else if( event.type == SDL_MOUSEWHEEL ){
419 vg.mouse_wheel[0] += event.wheel.preciseX;
420 vg.mouse_wheel[1] += event.wheel.preciseY;
421 }
422 else if( event.type == SDL_CONTROLLERAXISMOTION ||
423 event.type == SDL_CONTROLLERBUTTONDOWN ||
424 event.type == SDL_CONTROLLERBUTTONUP ||
425 event.type == SDL_CONTROLLERDEVICEADDED ||
426 event.type == SDL_CONTROLLERDEVICEREMOVED
427 )
428 {
429 vg_input_controller_event( &event );
430 }
431 else if( event.type == SDL_MOUSEMOTION ){
432 vg.mouse_delta[0] += event.motion.xrel;
433 vg.mouse_delta[1] += event.motion.yrel;
434 }
435 else if( event.type == SDL_WINDOWEVENT ){
436 if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ){
437 int w, h;
438 SDL_GL_GetDrawableSize( vg.window, &w, &h );
439
440 if( !w || !h ){
441 vg_warn( "Got a invalid framebuffer size: "
442 "%dx%d... ignoring\n", w, h );
443 }
444 else{
445 vg.window_x = w;
446 vg.window_y = h;
447
448 vg_framebuffer_resize(w,h);
449 }
450 }
451 else if( event.window.event == SDL_WINDOWEVENT_CLOSE ){
452 vg.window_should_close = 1;
453 }
454 }
455 else if( event.type == SDL_TEXTINPUT ){
456 console_proc_utf8( event.text.text );
457 }
458 }
459
460 vg.mouse_pos[0] += vg.mouse_delta[0];
461 vg.mouse_pos[1] += vg.mouse_delta[1];
462
463 /* Update input */
464 vg_update_inputs();
465 }
466
467 VG_STATIC void _vg_gameloop_update(void)
468 {
469 vg_profile_begin( &vg_prof_update );
470
471 vg.engine_stage = k_engine_stage_update;
472 vg_update();
473
474 /* Fixed update loop */
475 vg.engine_stage = k_engine_stage_update_fixed;
476 vg.accumulator += vg.time_delta;
477
478 vg.fixed_iterations = 0;
479 vg_lines.allow_input = 1;
480 while( vg.accumulator >= (VG_TIMESTEP_FIXED-0.00125) ){
481 vg_update_fixed();
482 vg_lines.allow_input = 0;
483
484 vg.accumulator -= VG_TIMESTEP_FIXED;
485 vg.accumulator = VG_MAX( 0.0, vg.accumulator );
486
487 vg.fixed_iterations ++;
488 if( vg.fixed_iterations == 8 ){
489 break;
490 }
491 }
492 vg_lines.allow_input = 1;
493
494 vg.engine_stage = k_engine_stage_update;
495 vg_update_post();
496 vg_profile_end( &vg_prof_update );
497 }
498
499 VG_STATIC void _vg_gameloop_render(void)
500 {
501 vg_profile_begin( &vg_prof_render );
502
503 if( vg.is_loaded ){
504 /* render */
505 vg.engine_stage = k_engine_stage_rendering;
506 vg_render();
507
508 /* ui */
509 vg.engine_stage = k_engine_stage_ui;
510 {
511 ui_begin( vg.window_x, vg.window_y );
512
513 /* TODO */
514 ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
515
516 vg_profile_drawn(
517 (struct vg_profile *[]){&vg_prof_update,&vg_prof_render}, 2,
518 (1.0f/(float)vg.refresh_rate)*1000.0f,
519 (ui_rect){ 4, 4, 250, 0 }, 0
520 );
521
522 if( vg_profiler ){
523 char perf[128];
524
525 snprintf( perf, 127,
526 "x: %d y: %d\n"
527 "refresh: %.1f (%.1fms)\n"
528 "samples: %d\n"
529 "iterations: %d (acc: %.3fms%%)\n",
530 vg.window_x, vg.window_y,
531 vg.refresh_rate, (1.0f/vg.refresh_rate)*1000.0f,
532 vg.samples,
533 vg.fixed_iterations,
534 (vg.accumulator/VG_TIMESTEP_FIXED)*100.0f );
535
536 ui_text( (ui_rect){258, 4+24+12,0,0},perf, 1,0);
537 }
538
539 /* FIXME */
540 audio_debug_ui( vg.pv );
541 vg_ui();
542 _vg_console_draw();
543
544 ui_resolve();
545 ui_draw( NULL );
546 }
547 }
548
549 vg_profile_end( &vg_prof_render );
550 }
551
552 VG_STATIC void _vg_gameloop(void)
553 {
554 vg.accumulator = 0.75f * (1.0f/60.0f);
555
556 int post_start = 0;
557 while(1){
558 _vg_process_events();
559
560 if( vg.window_should_close )
561 break;
562
563 /* scaled time */
564 vg.time_delta = vg.frame_delta * vg.time_rate;
565 vg.time += vg.time_delta;
566
567 if( vg.is_loaded ){
568 if( !post_start ){
569 vg_start();
570 post_start = 1;
571 }
572 }
573 else{
574 _vg_loader_render();
575 }
576
577 _vg_gameloop_update();
578 _vg_gameloop_render();
579
580 SDL_GL_SwapWindow( vg.window );
581 _vg_run_synced();
582 }
583 }
584
585 VG_STATIC void _vg_process_launch_opts_internal( int argc, char *argv[] )
586 {
587 char *arg;
588 while( vg_argp( argc, argv ) ){
589 if( (arg = vg_opt_arg( 'w' )) ){
590 vg.window_x = atoi( arg );
591 }
592
593 if( (arg = vg_opt_arg( 'h' )) ){
594 vg.window_y = atoi( arg );
595 }
596
597 if( (arg = vg_long_opt_arg( "samples" )) ){
598 vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
599 }
600
601 if( vg_long_opt( "use-libc-malloc" ) ){
602 vg_mem.use_libc_malloc = 1;
603 }
604
605 if( vg_long_opt( "high-performance" ) ){
606 vg.quality_profile = k_quality_profile_low;
607 }
608
609 vg_launch_opt();
610 }
611 }
612
613 VG_STATIC void _vg_init_window( const char *window_name )
614 {
615 vg_info( "SDL_INIT\n" );
616
617 if( SDL_Init( SDL_INIT_VIDEO ) != 0 ){
618 vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
619 exit(0);
620 }
621
622 SDL_InitSubSystem( SDL_INIT_AUDIO );
623 SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER );
624
625 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
626 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
627 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
628 SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK,
629 SDL_GL_CONTEXT_PROFILE_CORE );
630
631 SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR,
632 SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
633
634 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
635 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
636 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
637 SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
638 SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
639
640 /*
641 * Get monitor information
642 */
643 vg_info( "Getting display count\n" );
644 int display_count = 0, display_index = 0, mode_index = 0;
645 if( (display_count = SDL_GetNumVideoDisplays()) < 1 ){
646 vg_error( "SDL_GetNumVideoDisplays returned: %i\n", display_count );
647 exit(0);
648 }
649
650
651 #ifdef VG_DEVWINDOW
652 vg.refresh_rate = 60;
653 vg.window_x = 1000;
654 vg.window_y = 800;
655 #else
656 /* TODO: Allow chosing the modes at startup */
657 vg_info( "Getting default display mode\n" );
658 SDL_DisplayMode vm_ideal = { 0, 0, 0, 0, 0 };
659 if( SDL_GetDisplayMode( display_index, mode_index, &vm_ideal ) != 0 ){
660 vg_error( "SDL_GetDisplayMode failed: %s", SDL_GetError() );
661 exit(0);
662 }
663
664 if( vg.window_x )
665 vm_ideal.w = vg.window_x;
666
667 if( vg.window_y )
668 vm_ideal.h = vg.window_y;
669
670
671 SDL_DisplayMode vm_best;
672 if( !SDL_GetClosestDisplayMode( display_index, &vm_ideal, &vm_best ) ){
673 vg_error( "SDL_GetClosestDisplayMode failed: %s", SDL_GetError() );
674 exit(0);
675 }
676
677 vg.refresh_rate = vm_best.refresh_rate;
678 vg.window_x = vm_best.w;
679 vg.window_y = vm_best.h;
680 #endif
681
682 vg_info( "CreateWindow( %d %d @%.2fhz )\n", vg.window_x, vg.window_y,
683 vg.refresh_rate );
684
685 /* TODO: Allow selecting closest video mode from launch opts */
686 if((vg.window = SDL_CreateWindow( window_name,
687
688 #ifdef VG_DEVWINDOW
689 0, 0, vg.window_x, vg.window_y,
690 SDL_WINDOW_BORDERLESS|SDL_WINDOW_OPENGL|SDL_WINDOW_INPUT_GRABBED
691 ))){}
692 #else
693 SDL_WINDOWPOS_UNDEFINED,
694 SDL_WINDOWPOS_UNDEFINED,
695 vg.window_x, vg.window_y,
696
697 SDL_WINDOW_FULLSCREEN_DESKTOP |
698 SDL_WINDOW_OPENGL |
699 SDL_WINDOW_INPUT_GRABBED
700 )))
701 {
702 if( SDL_SetWindowDisplayMode( vg.window, &vm_best ) ){
703 vg_error( "SDL_SetWindowDisplayMode failed: %s", SDL_GetError() );
704 SDL_Quit();
705 exit(0);
706 }
707 }
708 #endif
709 else{
710 vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
711 exit(0);
712 }
713
714 vg_info( "CreateContext\n" );
715
716 /*
717 * OpenGL loading
718 */
719 if( (vg.gl_context = SDL_GL_CreateContext(vg.window) )){
720 SDL_GL_GetDrawableSize( vg.window, &vg.window_x, &vg.window_y );
721 vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
722 }
723 else{
724 vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
725 SDL_Quit();
726 exit(0);
727 }
728
729 if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) {
730 vg_error( "Glad Failed to initialize\n" );
731 SDL_GL_DeleteContext( vg.gl_context );
732 SDL_Quit();
733 exit(0);
734 }
735
736 const unsigned char* glver = glGetString( GL_VERSION );
737 vg_success( "Load setup complete, OpenGL version: %s\n", glver );
738
739 vg_info( "Setting swap interval\n" );
740
741 if( SDL_GL_SetSwapInterval( -1 ) == -1 ){
742 vg_warn( "Adaptive Vsync not supported\n" );
743
744 if( SDL_GL_SetSwapInterval( 1 ) == -1 ){
745 vg_fatal_exit_loop( "Cannot enable Vsync! You might be overriding it"
746 " in your graphics control panel.\n" );
747 }
748 else
749 vg_success( "Using vsync\n" );
750 }
751 else
752 vg_success( "Using adaptive Vsync\n" );
753
754 SDL_DisplayMode dispmode;
755 if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) ){
756 if( dispmode.refresh_rate ){
757 vg.refresh_rate = dispmode.refresh_rate;
758 vg_info( "Refresh rate: %d\n", dispmode.refresh_rate );
759 }
760 }
761 }
762
763 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
764 {
765 _vg_process_launch_opts_internal( argc, argv );
766
767 /* Systems init */
768 vg_alloc_quota();
769 _vg_log_init();
770 _vg_console_init();
771 _vg_init_window( window_name );
772
773 SDL_SetRelativeMouseMode(1);
774 vg.thread_id_main = SDL_GetThreadID(NULL);
775
776 /* Opengl-required systems */
777 _vg_ui_init();
778 _vg_loader_init();
779
780 vg.engine_status = k_engine_status_running;
781
782 _vg_opengl_sync_init();
783 vg_loader_start( _vg_load_full );
784 _vg_gameloop();
785
786 /* Shutdown */
787 _vg_console_write_persistent();
788
789 SDL_AtomicLock( &vg.sl_context );
790 vg.engine_status = k_engine_status_none;
791 SDL_AtomicUnlock( &vg.sl_context );
792
793 _vg_loader_free();
794
795 vg_success( "If you see this it means everything went.. \"well\".....\n" );
796
797 SDL_GL_DeleteContext( vg.gl_context );
798 SDL_Quit();
799 }
800
801 /*
802 * Immediately transfer away from calling thread into a safe loop, signal for
803 * others to shutdown, then free everything once the user closes the window.
804 *
805 * FIXME(bug): glfwWindowShouldClose() never returns 1 in windows via wine, ONLY
806 * when calling the program from outside its normal directory.
807 */
808 VG_STATIC void vg_fatal_exit_loop( const char *error )
809 {
810 /*
811 * https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
812 * thanks gnu <3
813 *
814 * TODO: this on windows?
815 */
816
817 vg_print_backtrace();
818 vg_error( "Fatal error: %s\n", error );
819
820 SDL_AtomicLock( &vg.sl_context );
821 if( vg.engine_status == k_engine_status_none ){
822 SDL_AtomicUnlock( &vg.sl_context );
823
824 /* TODO: Correct shutdown before other systems */
825 exit(0);
826 }
827 else{
828 SDL_AtomicUnlock( &vg.sl_context );
829
830 vg_acquire_thread_sync();
831
832 SDL_AtomicLock( &vg.sl_context );
833 vg.engine_status = k_engine_status_crashed;
834 vg.str_const_engine_err = error;
835 SDL_AtomicUnlock( &vg.sl_context );
836
837 /* Notify other thread for curtosey */
838 if( vg_thread_purpose() == k_thread_purpose_main ){
839 SDL_AtomicLock( &vg.sl_context );
840
841 if( vg.exec_context != 0 )
842 SDL_SemPost( vg.sem_allow_exec );
843
844 SDL_AtomicUnlock( &vg.sl_context );
845 }
846
847 vg_audio_free();
848
849 while(1){
850 _vg_process_events();
851 if( vg.window_should_close )
852 break;
853
854 if( vg_getkey( SDLK_ESCAPE ) )
855 break;
856
857 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
858 glEnable(GL_BLEND);
859 glDisable(GL_DEPTH_TEST);
860 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
861 glBlendEquation(GL_FUNC_ADD);
862
863 glClearColor( 0.15f + sinf(vg.time_real)*0.1f, 0.0f, 0.0f,1.0f );
864 glClear( GL_COLOR_BUFFER_BIT );
865 glViewport( 0,0, vg.window_x, vg.window_y );
866
867 _vg_render_log();
868 SDL_GL_SwapWindow( vg.window );
869 }
870
871 /* Can now shutdown and EXIT */
872 _vg_loader_free();
873 SDL_GL_DeleteContext( vg.gl_context );
874 SDL_Quit();
875 exit(0);
876 }
877 }
878
879 #else /* VG_GAME */
880
881 VG_STATIC void vg_fatal_exit_loop( const char *error )
882 {
883 vg_error( "Fatal error: %s\n", error );
884 exit(0);
885 }
886
887 #endif /* VG_GAME */
888
889 /*
890 * Graphic cards will check these to force it to use the GPU
891 */
892 u32 NvOptimusEnablement = 0x00000001;
893 int AmdPowerXpressRequestHighPerformance = 1;
894
895 #endif /* VG_HEADER_H */