bad char
[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 #if 0
377 vg_function_push( (struct vg_cmd)
378 {
379 .name = "shaders",
380 .function = vg_shaders_live_recompile
381 });
382 #endif
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 {
416 if( event.type == SDL_KEYDOWN )
417 {
418 console_proc_key( event.key.keysym );
419 }
420 else if( event.type == SDL_MOUSEWHEEL )
421 {
422 vg.mouse_wheel[0] += event.wheel.preciseX;
423 vg.mouse_wheel[1] += event.wheel.preciseY;
424 }
425 else if( event.type == SDL_CONTROLLERAXISMOTION ||
426 event.type == SDL_CONTROLLERBUTTONDOWN ||
427 event.type == SDL_CONTROLLERBUTTONUP ||
428 event.type == SDL_CONTROLLERDEVICEADDED ||
429 event.type == SDL_CONTROLLERDEVICEREMOVED
430 )
431 {
432 vg_input_controller_event( &event );
433 }
434 else if( event.type == SDL_MOUSEMOTION )
435 {
436 vg.mouse_delta[0] += event.motion.xrel;
437 vg.mouse_delta[1] += event.motion.yrel;
438 }
439 else if( event.type == SDL_WINDOWEVENT )
440 {
441 if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED )
442 {
443 int w, h;
444 SDL_GL_GetDrawableSize( vg.window, &w, &h );
445
446 if( !w || !h )
447 {
448 vg_warn( "Got a invalid framebuffer size: "
449 "%dx%d... ignoring\n", w, h );
450 }
451 else
452 {
453 vg.window_x = w;
454 vg.window_y = h;
455
456 vg_framebuffer_resize(w,h);
457 }
458 }
459 else if( event.window.event == SDL_WINDOWEVENT_CLOSE )
460 {
461 vg.window_should_close = 1;
462 }
463 }
464 else if( event.type == SDL_TEXTINPUT )
465 {
466 console_proc_utf8( event.text.text );
467 }
468 }
469
470 vg.mouse_pos[0] += vg.mouse_delta[0];
471 vg.mouse_pos[1] += vg.mouse_delta[1];
472
473 /* Update input */
474 vg_update_inputs();
475 }
476
477 VG_STATIC void _vg_gameloop_update(void)
478 {
479 vg_profile_begin( &vg_prof_update );
480
481 vg.engine_stage = k_engine_stage_update;
482 vg_update();
483
484 /* Fixed update loop */
485 vg.engine_stage = k_engine_stage_update_fixed;
486 vg.accumulator += vg.time_delta;
487
488 vg.fixed_iterations = 0;
489 vg_lines.allow_input = 1;
490 while( vg.accumulator >= (VG_TIMESTEP_FIXED-0.00125) )
491 {
492 vg_update_fixed();
493 vg_lines.allow_input = 0;
494
495 vg.accumulator -= VG_TIMESTEP_FIXED;
496 vg.accumulator = VG_MAX( 0.0, vg.accumulator );
497
498 vg.fixed_iterations ++;
499 if( vg.fixed_iterations == 8 )
500 {
501 break;
502 }
503 }
504 vg_lines.allow_input = 1;
505
506 vg.engine_stage = k_engine_stage_update;
507 vg_update_post();
508 vg_profile_end( &vg_prof_update );
509 }
510
511 VG_STATIC void _vg_gameloop_render(void)
512 {
513 vg_profile_begin( &vg_prof_render );
514
515 if( vg.is_loaded )
516 {
517 /* render */
518 vg.engine_stage = k_engine_stage_rendering;
519 vg_render();
520
521 /* ui */
522 vg.engine_stage = k_engine_stage_ui;
523 {
524 ui_begin( vg.window_x, vg.window_y );
525
526 /* TODO */
527 ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
528
529 vg_profile_drawn(
530 (struct vg_profile *[]){&vg_prof_update,&vg_prof_render}, 2,
531 (1.0f/(float)vg.refresh_rate)*1000.0f,
532 (ui_rect){ 4, 4, 250, 0 }, 0
533 );
534
535 if( vg_profiler )
536 {
537
538 char perf[128];
539
540 snprintf( perf, 127,
541 "x: %d y: %d\n"
542 "refresh: %.1f (%.1fms)\n"
543 "samples: %d\n"
544 "iterations: %d (acc: %.3fms%%)\n",
545 vg.window_x, vg.window_y,
546 vg.refresh_rate, (1.0f/vg.refresh_rate)*1000.0f,
547 vg.samples,
548 vg.fixed_iterations,
549 (vg.accumulator/VG_TIMESTEP_FIXED)*100.0f );
550
551 ui_text( (ui_rect){258, 4+24+12,0,0},perf, 1,0);
552 }
553
554 /* FIXME */
555 #if 0
556 audio_debug_ui( vg.pv );
557 #endif
558 vg_ui();
559 _vg_console_draw();
560
561 ui_resolve();
562 ui_draw( NULL );
563 }
564 }
565
566 vg_profile_end( &vg_prof_render );
567 }
568
569 VG_STATIC void _vg_gameloop(void)
570 {
571 vg.accumulator = 0.75f * (1.0f/60.0f);
572
573 int post_start = 0;
574 while(1)
575 {
576 _vg_process_events();
577
578 if( vg.window_should_close )
579 break;
580
581 /* scaled time */
582 vg.time_delta = vg.frame_delta * vg.time_rate;
583 vg.time += vg.time_delta;
584
585 if( vg.is_loaded )
586 {
587 if( !post_start )
588 {
589 vg_start();
590 post_start = 1;
591 }
592 }
593 else
594 {
595 _vg_loader_render();
596 }
597
598 _vg_gameloop_update();
599 _vg_gameloop_render();
600 audio_push_console_vol();
601
602 SDL_GL_SwapWindow( vg.window );
603 _vg_run_synced();
604 }
605 }
606
607 VG_STATIC void _vg_process_launch_opts_internal( int argc, char *argv[] )
608 {
609 char *arg;
610 while( vg_argp( argc, argv ) )
611 {
612 if( (arg = vg_opt_arg( 'w' )) )
613 {
614 vg.window_x = atoi( arg );
615 }
616
617 if( (arg = vg_opt_arg( 'h' )) )
618 {
619 vg.window_y = atoi( arg );
620 }
621
622 if( (arg = vg_long_opt_arg( "samples" )) )
623 {
624 vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
625 }
626
627 if( vg_long_opt( "use-libc-malloc" ) )
628 {
629 vg_mem.use_libc_malloc = atoi( arg );
630 }
631
632 if( vg_long_opt( "high-performance" ) )
633 {
634 vg.quality_profile = k_quality_profile_low;
635 }
636
637 vg_launch_opt();
638 }
639 }
640
641 VG_STATIC void _vg_init_window( const char *window_name )
642 {
643 if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_GAMECONTROLLER) != 0 )
644 {
645 vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
646 exit(0);
647 }
648
649 SDL_GL_SetSwapInterval( 1 );
650 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
651 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
652 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
653 SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK,
654 SDL_GL_CONTEXT_PROFILE_CORE );
655
656 SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR,
657 SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
658
659 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
660 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
661 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
662 SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
663 SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
664
665 /*
666 * Get monitor information
667 */
668 int display_count = 0, display_index = 0, mode_index = 0;
669 if( (display_count = SDL_GetNumVideoDisplays()) < 1 )
670 {
671 vg_error( "SDL_GetNumVideoDisplays returned: %i\n", display_count );
672 exit(0);
673 }
674
675 /* TODO: Allow chosing the modes at startup */
676 SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
677 if( SDL_GetDisplayMode( display_index, mode_index, &mode ) != 0 )
678 {
679 vg_error( "SDL_GetDisplayMode failed: %s", SDL_GetError() );
680 exit(0);
681 }
682
683 vg.refresh_rate = mode.refresh_rate;
684
685 /* TODO: Allow selecting closest video mode from launch opts */
686 if((vg.window = SDL_CreateWindow( window_name,
687 SDL_WINDOWPOS_UNDEFINED,
688 SDL_WINDOWPOS_UNDEFINED,
689 mode.w, mode.h,
690
691 SDL_WINDOW_FULLSCREEN_DESKTOP |
692 SDL_WINDOW_OPENGL |
693 SDL_WINDOW_INPUT_GRABBED )))
694 {
695 }
696 else
697 {
698 vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
699 exit(0);
700 }
701
702 /*
703 * OpenGL loading
704 */
705 if( (vg.gl_context = SDL_GL_CreateContext(vg.window) ))
706 {
707 SDL_GL_GetDrawableSize( vg.window, &vg.window_x, &vg.window_y );
708 vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
709 }
710 else
711 {
712 vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
713 SDL_Quit();
714 exit(0);
715 }
716
717 if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) )
718 {
719 vg_error( "Glad Failed to initialize\n" );
720 SDL_GL_DeleteContext( vg.gl_context );
721 SDL_Quit();
722 exit(0);
723 }
724
725 const unsigned char* glver = glGetString( GL_VERSION );
726 vg_success( "Load setup complete, OpenGL version: %s\n", glver );
727 }
728
729 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
730 {
731 _vg_process_launch_opts_internal( argc, argv );
732
733 /* Systems init */
734 vg_alloc_quota();
735 _vg_log_init();
736 _vg_console_init();
737 _vg_init_window( window_name );
738
739 SDL_SetRelativeMouseMode(1);
740 vg.thread_id_main = SDL_GetThreadID(NULL);
741
742 /* Opengl-required systems */
743 _vg_ui_init();
744 _vg_loader_init();
745
746 vg.engine_status = k_engine_status_running;
747
748 _vg_opengl_sync_init();
749 vg_loader_start( _vg_load_full );
750 _vg_gameloop();
751
752 /* Shutdown */
753 _vg_console_write_persistent();
754
755 SDL_AtomicLock( &vg.sl_context );
756 vg.engine_status = k_engine_status_none;
757 SDL_AtomicUnlock( &vg.sl_context );
758
759 _vg_loader_free();
760
761 vg_success( "If you see this it means everything went.. \"well\".....\n" );
762
763 SDL_GL_DeleteContext( vg.gl_context );
764 SDL_Quit();
765 }
766
767 /*
768 * Immediately transfer away from calling thread into a safe loop, signal for
769 * others to shutdown, then free everything once the user closes the window.
770 *
771 * FIXME(bug): glfwWindowShouldClose() never returns 1 in windows via wine, ONLY
772 * when calling the program from outside its normal directory.
773 */
774 VG_STATIC void vg_fatal_exit_loop( const char *error )
775 {
776 /*
777 * https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
778 * thanks gnu <3
779 *
780 * TODO: this on windows?
781 */
782
783 #ifndef _WIN32
784
785 void *array[20];
786 char **strings;
787 int size, i;
788
789 size = backtrace( array, 20 );
790 strings = backtrace_symbols( array, size );
791
792 if( strings != NULL )
793 {
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 vg_error( "Fatal error: %s\n", error );
807
808 SDL_AtomicLock( &vg.sl_context );
809 if( vg.engine_status == k_engine_status_none )
810 {
811 SDL_AtomicUnlock( &vg.sl_context );
812
813 /* TODO: Correct shutdown before other systems */
814 exit(0);
815 }
816 else
817 {
818 SDL_AtomicUnlock( &vg.sl_context );
819
820 vg_acquire_thread_sync();
821
822 SDL_AtomicLock( &vg.sl_context );
823 vg.engine_status = k_engine_status_crashed;
824 vg.str_const_engine_err = error;
825 SDL_AtomicUnlock( &vg.sl_context );
826
827 /* Notify other thread for curtosey */
828 if( vg_thread_purpose() == k_thread_purpose_main )
829 {
830 SDL_AtomicLock( &vg.sl_context );
831
832 if( vg.exec_context != 0 )
833 SDL_SemPost( vg.sem_allow_exec );
834
835 SDL_AtomicUnlock( &vg.sl_context );
836 }
837
838 vg_audio_free();
839
840 while(1)
841 {
842 _vg_process_events();
843 if( vg.window_should_close )
844 break;
845
846 if( vg_getkey( SDLK_ESCAPE ) )
847 break;
848
849 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
850 glEnable(GL_BLEND);
851 glDisable(GL_DEPTH_TEST);
852 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
853 glBlendEquation(GL_FUNC_ADD);
854
855 glClearColor( 0.15f + sinf(vg.time_real)*0.1f, 0.0f, 0.0f,1.0f );
856 glClear( GL_COLOR_BUFFER_BIT );
857 glViewport( 0,0, vg.window_x, vg.window_y );
858
859 _vg_render_log();
860 SDL_GL_SwapWindow( vg.window );
861 }
862
863 /* Can now shutdown and EXIT */
864 _vg_loader_free();
865 SDL_GL_DeleteContext( vg.gl_context );
866 SDL_Quit();
867 exit(0);
868 }
869 }
870
871 #else /* VG_GAME */
872
873 VG_STATIC void vg_fatal_exit_loop( const char *error )
874 {
875 vg_error( "Fatal error: %s\n", error );
876 exit(0);
877 }
878
879 #endif /* VG_GAME */
880
881 /*
882 * Graphic cards will check these to force it to use the GPU
883 */
884 u32 NvOptimusEnablement = 0x00000001;
885 int AmdPowerXpressRequestHighPerformance = 1;
886
887 #endif /* VG_HEADER_H */