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