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