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