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