switch to async system
[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 #ifndef VG_HEADER_H
53 #define VG_HEADER_H
54
55 #include "vg_platform.h"
56 #include "vg_mem.h"
57
58 #ifndef _WIN32
59 #include <execinfo.h>
60 #endif
61
62 VG_STATIC void vg_print_backtrace(void)
63 {
64 #ifndef _WIN32
65
66 void *array[20];
67 char **strings;
68 int size, i;
69
70 size = backtrace( array, 20 );
71 strings = backtrace_symbols( array, size );
72
73 if( strings != NULL ){
74 vg_error( "---------------- gnu backtrace -------------\n" );
75
76 for( int i=0; i<size; i++ )
77 vg_info( "%s\n", strings[i] );
78
79 vg_error( "---------------- gnu backtrace -------------\n" );
80 }
81
82 free( strings );
83
84 #endif
85 }
86
87 #ifdef VG_GAME
88 #include "dep/glad/glad.h"
89 #include "submodules/SDL/include/SDL.h"
90 #include "vg_stdint.h"
91
92 void vg_register_exit( void( *funcptr )(void), const char *name );
93
94 #include "vg_m.h"
95 #include "vg_io.h"
96 #include "vg_log.h"
97 #include "vg_steam.h"
98 #include <setjmp.h>
99
100 //#define VG_SYNC_DEBUG
101 #ifdef VG_SYNC_DEBUG
102 #define VG_SYNC_LOG(STR,...) \
103 vg_info(STR,SDL_GetThreadID(NULL),##__VA_ARGS__)
104 #else
105 #define VG_SYNC_LOG(...)
106 #endif
107
108 /* API */
109 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name );
110
111 /* Thread 1 */
112 VG_STATIC void vg_preload(void);
113 VG_STATIC void vg_load(void);
114
115 /* Main thread */
116 VG_STATIC void vg_launch_opt(void);
117 VG_STATIC void vg_start(void);
118
119 VG_STATIC void vg_framebuffer_resize(int w, int h);
120 VG_STATIC void vg_update(void);
121 VG_STATIC void vg_update_fixed(void);
122 VG_STATIC void vg_update_post(void);
123
124 VG_STATIC void vg_render(void);
125 VG_STATIC void vg_ui(void);
126
127 struct vg
128 {
129 /* Engine sync */
130 SDL_Window *window;
131 SDL_GLContext gl_context;
132
133 SDL_sem *sem_loader; /* allows only one loader at a time */
134
135 jmp_buf env_loader_exit;
136
137 SDL_threadID thread_id_main,
138 thread_id_loader;
139
140 SDL_SpinLock sl_status;
141 enum engine_status
142 {
143 k_engine_status_none,
144 k_engine_status_load_internal,
145 k_engine_status_running,
146 k_engine_status_crashed
147 }
148 engine_status;
149
150 /* Window information */
151 int window_x,
152 window_y,
153 samples,
154 window_should_close;
155
156 int display_refresh_rate,
157 fps_limit; /* 0: use vsync, >0: cap fps to this, no vsync */
158
159 enum vsync_feature
160 {
161 k_vsync_feature_disabled=0,
162 k_vsync_feature_enabled=1,
163 k_vsync_feature_enabled_adaptive=2,
164 k_vsync_feature_error=3
165 }
166 vsync_feature;
167
168 double mouse_pos[2];
169 v2f mouse_delta,
170 mouse_wheel;
171
172 /* Runtime */
173 double time,
174 time_delta,
175 time_rate,
176
177 time_fixed_accumulator,
178 time_fixed_extrapolate,
179 time_frame_delta;
180
181 u64 time_hp, time_hp_last, time_spinning;
182
183 int fixed_iterations;
184
185 enum engine_stage
186 {
187 k_engine_stage_none,
188 k_engine_stage_update,
189 k_engine_stage_update_fixed,
190 k_engine_stage_rendering,
191 k_engine_stage_ui
192 }
193 engine_stage;
194
195 /* graphics */
196 m4x4f pv;
197 enum quality_profile
198 {
199 k_quality_profile_high = 0,
200 k_quality_profile_low = 1,
201 }
202 quality_profile;
203 }
204 VG_STATIC vg = { .time_rate = 1.0 };
205
206 enum vg_thread_purpose
207 {
208 k_thread_purpose_nothing,
209 k_thread_purpose_main,
210 k_thread_purpose_loader
211 };
212
213 #include "vg_async.h"
214
215 VG_STATIC enum engine_status _vg_engine_status(void)
216 {
217 SDL_AtomicLock( &vg.sl_status );
218 enum engine_status status = vg.engine_status;
219 SDL_AtomicUnlock( &vg.sl_status );
220
221 return status;
222 }
223
224 VG_STATIC enum vg_thread_purpose vg_thread_purpose(void)
225 {
226 SDL_AtomicLock( &vg.sl_status );
227
228 if( vg.thread_id_main == SDL_GetThreadID(NULL) ){
229 SDL_AtomicUnlock( &vg.sl_status );
230 return k_thread_purpose_main;
231 }
232 else{
233 SDL_AtomicUnlock( &vg.sl_status );
234 return k_thread_purpose_loader;
235 }
236 }
237
238 VG_STATIC void _vg_opengl_sync_init(void)
239 {
240 vg.sem_loader = SDL_CreateSemaphore(1);
241 }
242
243 VG_STATIC void vg_checkgl( const char *src_info );
244 #define VG_STRINGIT( X ) #X
245 #define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
246
247 #include "vg_console.h"
248 #include "vg_profiler.h"
249 #include "vg_audio.h"
250 #include "vg_shader.h"
251 #include "vg_tex.h"
252 #include "vg_input.h"
253 #include "vg_ui.h"
254 #include "vg_lines.h"
255 #include "vg_loader.h"
256 #include "vg_opt.h"
257
258 /* Diagnostic */
259 VG_STATIC struct vg_profile vg_prof_update = {.name="update()"},
260 vg_prof_render = {.name="render()"},
261 vg_prof_swap = {.name="swap"};
262
263 VG_STATIC void vg_checkgl( const char *src_info )
264 {
265 int fail = 0;
266
267 GLenum err;
268 while( (err = glGetError()) != GL_NO_ERROR ){
269 vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
270 fail = 1;
271 }
272
273 if( fail )
274 vg_fatal_error( "OpenGL Error" );
275 }
276
277 VG_STATIC void async_vg_bake_shaders( void *payload, u32 size )
278 {
279 vg_shaders_compile();
280 }
281
282 VG_STATIC void vg_bake_shaders(void)
283 {
284 vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile, NULL );
285
286 vg_async_item *call = vg_async_alloc(0);
287 vg_async_dispatch( call, async_vg_bake_shaders );
288 }
289
290 void async_internal_complete( void *payload, u32 size )
291 {
292 vg_success( "Internal async setup complete\n" );
293 SDL_AtomicLock( &vg.sl_status );
294
295 if( vg.engine_status == k_engine_status_crashed ){
296 SDL_AtomicUnlock( &vg.sl_status );
297 return;
298 }
299 else{
300 vg.engine_status = k_engine_status_running;
301 }
302
303 SDL_AtomicUnlock( &vg.sl_status );
304 }
305
306 VG_STATIC void _vg_load_full(void)
307 {
308 vg_preload();
309
310 /* internal */
311 vg_loader_step( vg_input_init, vg_input_free );
312 vg_loader_step( vg_lines_init, NULL );
313 vg_loader_step( vg_audio_init, vg_audio_free );
314 vg_loader_step( vg_profiler_init, NULL );
315
316 vg_async_item *test_async = vg_async_alloc( 0 );
317 vg_async_dispatch( test_async, async_internal_complete );
318
319 /* client */
320 vg_load();
321 }
322
323 VG_STATIC void _vg_process_events(void)
324 {
325 v2_zero( vg.mouse_wheel );
326 v2_zero( vg.mouse_delta );
327
328 /* SDL event loop */
329 SDL_Event event;
330 while( SDL_PollEvent( &event ) ){
331 if( event.type == SDL_KEYDOWN ){
332 console_proc_key( event.key.keysym );
333 }
334 else if( event.type == SDL_MOUSEWHEEL ){
335 vg.mouse_wheel[0] += event.wheel.preciseX;
336 vg.mouse_wheel[1] += event.wheel.preciseY;
337 }
338 else if( event.type == SDL_CONTROLLERAXISMOTION ||
339 event.type == SDL_CONTROLLERBUTTONDOWN ||
340 event.type == SDL_CONTROLLERBUTTONUP ||
341 event.type == SDL_CONTROLLERDEVICEADDED ||
342 event.type == SDL_CONTROLLERDEVICEREMOVED
343 )
344 {
345 vg_input_controller_event( &event );
346 }
347 else if( event.type == SDL_MOUSEMOTION ){
348 vg.mouse_delta[0] += event.motion.xrel;
349 vg.mouse_delta[1] += event.motion.yrel;
350 }
351 else if( event.type == SDL_WINDOWEVENT ){
352 if( event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ){
353 int w, h;
354 SDL_GL_GetDrawableSize( vg.window, &w, &h );
355
356 if( !w || !h ){
357 vg_warn( "Got a invalid framebuffer size: "
358 "%dx%d... ignoring\n", w, h );
359 }
360 else{
361 vg.window_x = w;
362 vg.window_y = h;
363
364 vg_framebuffer_resize(w,h);
365 }
366 }
367 else if( event.window.event == SDL_WINDOWEVENT_CLOSE ){
368 vg.window_should_close = 1;
369 }
370 }
371 else if( event.type == SDL_TEXTINPUT ){
372 console_proc_utf8( event.text.text );
373 }
374 }
375
376 vg.mouse_pos[0] += vg.mouse_delta[0];
377 vg.mouse_pos[1] += vg.mouse_delta[1];
378
379 /* Update input */
380 vg_update_inputs();
381 }
382
383 VG_STATIC void _vg_gameloop_update(void)
384 {
385 vg_profile_begin( &vg_prof_update );
386
387 vg.engine_stage = k_engine_stage_update;
388 vg_update();
389
390 /* Fixed update loop */
391 vg.engine_stage = k_engine_stage_update_fixed;
392
393 vg.fixed_iterations = 0;
394 vg_lines.allow_input = 1;
395 vg.time_fixed_accumulator += vg.time_delta;
396
397 while( vg.time_fixed_accumulator >= VG_TIMESTEP_FIXED ){
398 vg_update_fixed();
399 vg_lines.allow_input = 0;
400
401 vg.time_fixed_accumulator -= VG_TIMESTEP_FIXED;
402 //vg.accumulator = VG_MAX( 0.0, vg.accumulator );
403
404 vg.fixed_iterations ++;
405 if( vg.fixed_iterations == 8 ){
406 break;
407 }
408 }
409 vg_lines.allow_input = 1;
410 vg.time_fixed_extrapolate = vg.time_fixed_accumulator / VG_TIMESTEP_FIXED;
411
412 vg.engine_stage = k_engine_stage_update;
413 vg_update_post();
414 vg_profile_end( &vg_prof_update );
415 }
416
417 VG_STATIC void _vg_gameloop_render(void)
418 {
419 vg_profile_begin( &vg_prof_render );
420
421 /* render */
422 vg.engine_stage = k_engine_stage_rendering;
423 vg_render();
424
425 /* ui */
426 vg.engine_stage = k_engine_stage_ui;
427 {
428 ui_begin( vg.window_x, vg.window_y );
429
430 /* TODO */
431 ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
432
433 int frame_target = vg.display_refresh_rate;
434
435 if( vg.fps_limit > 0 ){
436 frame_target = vg.fps_limit;
437 }
438
439 vg_profile_drawn(
440 (struct vg_profile *[]){
441 &vg_prof_update,&vg_prof_render,&vg_prof_swap}, 3,
442 (1.0f/(float)frame_target)*1000.0f,
443 (ui_rect){ 4, 4, 250, 0 }, 0
444 );
445
446 if( vg_profiler ){
447 char perf[256];
448
449 snprintf( perf, 255,
450 "x: %d y: %d\n"
451 "refresh: %d (%.1fms)\n"
452 "samples: %d\n"
453 "iterations: %d (acc: %.3fms%%)\n"
454 "time: real(%.2f) delta(%.2f) rate(%.2f)\n"
455 #ifdef _WIN32
456 " extrap(%.2f) frame(%.2f) spin( %llu )\n",
457 #else
458 " extrap(%.2f) frame(%.2f) spin( %lu )\n",
459 #endif
460 vg.window_x, vg.window_y,
461 frame_target, (1.0f/(float)frame_target)*1000.0f,
462 vg.samples,
463 vg.fixed_iterations,
464 (vg.time_fixed_accumulator/VG_TIMESTEP_FIXED)*100.0f,
465 vg.time, vg.time_delta, vg.time_rate,
466 vg.time_fixed_extrapolate, vg.time_frame_delta,
467 vg.time_spinning );
468
469 ui_text( (ui_rect){258, 4+24+12+12,0,0},perf, 1,0);
470 }
471
472 /* FIXME */
473 audio_debug_ui( vg.pv );
474 vg_ui();
475 _vg_console_draw();
476
477 ui_resolve();
478 ui_draw( NULL );
479 }
480
481 vg_profile_end( &vg_prof_render );
482 }
483
484 VG_STATIC int vg_framefilter( double dt )
485 {
486 if( (vg.fps_limit <= 0) && (vg.vsync_feature != k_vsync_feature_error) ){
487 /* turn on vsync if not enabled */
488
489 enum vsync_feature requested = k_vsync_feature_enabled;
490 if( vg.fps_limit < 0 ) requested = k_vsync_feature_enabled_adaptive;
491
492 if( vg.vsync_feature != requested ){
493 vg_info( "Setting swap interval\n" );
494
495 int swap_interval = 1;
496 if( requested == k_vsync_feature_enabled_adaptive ) swap_interval = -1;
497
498 if( SDL_GL_SetSwapInterval( swap_interval ) == -1 ){
499 if( requested == k_vsync_feature_enabled ){
500 vg_error( "Vsync is not supported by your system\n" );
501 vg_warn( "You may be overriding it in your"
502 " graphics control panel.\n" );
503 }
504 else{
505 vg_error( "Adaptive Vsync is not supported by your system\n" );
506 }
507
508 vg.vsync_feature = k_vsync_feature_error;
509 vg.fps_limit = vg.display_refresh_rate;
510
511 /* TODO: Make popup to notify user that this happened */
512 return 1;
513 }
514 else{
515 vg_success( "Vsync enabled (%d)\n", requested );
516 vg.vsync_feature = requested;
517 }
518 }
519
520 return 0;
521 }
522
523 if( vg.vsync_feature != k_vsync_feature_disabled ){
524 SDL_GL_SetSwapInterval( 0 );
525 vg.vsync_feature = k_vsync_feature_disabled;
526 }
527
528 if( vg.fps_limit < 25 ) vg.fps_limit = 25;
529 if( vg.fps_limit > 300 ) vg.fps_limit = 300;
530
531 double min_frametime = 1.0/(double)vg.fps_limit;
532 if( vg.time_frame_delta < min_frametime ){
533 /* TODO: we can use high res nanosleep on Linux here */
534 double sleep_ms = (min_frametime-vg.time_frame_delta) * 1000.0;
535 u32 ms = (u32)floor( sleep_ms );
536
537 if( ms ){
538 SDL_Delay( ms );
539 }
540 else{
541 vg.time_spinning ++;
542 }
543
544 return 1;
545 }
546
547 return 0;
548 }
549
550 VG_STATIC int _vg_crashscreen(void)
551 {
552 if( vg.window_should_close )
553 return 1;
554
555 if( vg_getkey( SDLK_ESCAPE ) )
556 return 1;
557
558 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
559 glEnable(GL_BLEND);
560 glDisable(GL_DEPTH_TEST);
561 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
562 glBlendEquation(GL_FUNC_ADD);
563
564 glClearColor( 0.15f + sinf(vg.time)*0.1f, 0.0f, 0.0f,1.0f );
565 glClear( GL_COLOR_BUFFER_BIT );
566 glViewport( 0,0, vg.window_x, vg.window_y );
567
568 _vg_render_log();
569
570 return 0;
571 }
572
573 VG_STATIC void _vg_gameloop(void)
574 {
575 //vg.time_fixed_accumulator = 0.75f * (1.0f/60.0f);
576
577 vg.time_hp = SDL_GetPerformanceCounter();
578 vg.time_hp_last = vg.time_hp;
579
580 int post_start = 0;
581 while(1){
582
583 vg.time_hp = SDL_GetPerformanceCounter();
584 u64 udt = vg.time_hp - vg.time_hp_last;
585 vg.time_hp_last = vg.time_hp;
586
587 double dt = (double)udt / (double)SDL_GetPerformanceFrequency();
588
589 vg.time_frame_delta += dt;
590
591 if( vg_framefilter( dt ) )
592 continue;
593
594 vg_profile_begin( &vg_prof_swap );
595 SDL_GL_SwapWindow( vg.window );
596 vg_profile_end( &vg_prof_swap );
597
598 enum engine_status status = _vg_engine_status();
599
600 if( status == k_engine_status_crashed ){
601 if( _vg_crashscreen() )
602 break;
603 }
604 else{
605 vg.time_delta = vg.time_frame_delta * vg.time_rate;
606 vg.time += vg.time_delta;
607
608 vg_run_async_checked();
609 _vg_process_events();
610
611 if( vg.window_should_close )
612 break;
613
614 if( status == k_engine_status_running ){
615 _vg_gameloop_update();
616 _vg_gameloop_render();
617 }
618 else{
619 _vg_loader_render();
620 }
621 }
622
623 vg.time_frame_delta = 0.0;
624 vg.time_spinning = 0;
625 }
626 }
627
628 VG_STATIC void _vg_process_launch_opts_internal( int argc, char *argv[] )
629 {
630 char *arg;
631 while( vg_argp( argc, argv ) ){
632 if( (arg = vg_opt_arg( 'w' )) ){
633 vg.window_x = atoi( arg );
634 }
635
636 if( (arg = vg_opt_arg( 'h' )) ){
637 vg.window_y = atoi( arg );
638 }
639
640 if( (arg = vg_long_opt_arg( "samples" )) ){
641 vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
642 }
643
644 if( vg_long_opt( "use-libc-malloc" ) ){
645 vg_mem.use_libc_malloc = 1;
646 }
647
648 if( vg_long_opt( "high-performance" ) ){
649 vg.quality_profile = k_quality_profile_low;
650 }
651
652 vg_launch_opt();
653 }
654 }
655
656 VG_STATIC void _vg_init_window( const char *window_name )
657 {
658 vg_info( "SDL_INIT\n" );
659
660 if( SDL_Init( SDL_INIT_VIDEO ) != 0 ){
661 vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
662 exit(0);
663 }
664
665 SDL_InitSubSystem( SDL_INIT_AUDIO );
666 SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER );
667
668 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
669 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
670 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 3 );
671 SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK,
672 SDL_GL_CONTEXT_PROFILE_CORE );
673
674 SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR,
675 SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH );
676
677 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
678 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
679 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
680 SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
681 SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 0 );
682
683 /*
684 * Get monitor information
685 */
686 vg_info( "Getting display count\n" );
687 int display_count = 0,
688 display_index = 0,
689 mode_index = 0;
690
691 #ifdef VG_DEVWINDOW
692 vg.window_x = 1600;
693 vg.window_y = 1000;
694 #else
695
696 SDL_DisplayMode video_mode;
697 if( SDL_GetDesktopDisplayMode( display_index, &video_mode ) ){
698 vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() );
699 SDL_Quit();
700 exit(0);
701 }
702
703 vg.display_refresh_rate = video_mode.refresh_rate;
704 vg.window_x = video_mode.w;
705 vg.window_y = video_mode.h;
706 #endif
707
708 #ifndef _WIN32
709 SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" );
710 SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" );
711 SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" );
712 #endif
713
714 vg_info( "CreateWindow( %d %d @%dhz )\n", vg.window_x, vg.window_y,
715 vg.display_refresh_rate );
716
717 /* TODO: Allow selecting closest video mode from launch opts */
718 if((vg.window = SDL_CreateWindow( window_name,
719
720 #ifdef VG_DEVWINDOW
721 0, 0, vg.window_x, vg.window_y,
722 SDL_WINDOW_BORDERLESS|SDL_WINDOW_OPENGL|SDL_WINDOW_INPUT_GRABBED
723 ))){}
724 #else
725 0, 0,
726 vg.window_x, vg.window_y,
727
728 SDL_WINDOW_FULLSCREEN_DESKTOP |
729 SDL_WINDOW_OPENGL |
730 SDL_WINDOW_INPUT_GRABBED
731 )))
732 {
733 if( SDL_SetWindowDisplayMode( vg.window, &video_mode ) ){
734 vg_error( "SDL_SetWindowDisplayMode failed: %s", SDL_GetError() );
735 SDL_Quit();
736 exit(0);
737 }
738 }
739 #endif
740 else{
741 vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
742 exit(0);
743 }
744
745 SDL_RaiseWindow( vg.window );
746
747 vg_info( "CreateContext\n" );
748
749 /*
750 * OpenGL loading
751 */
752 if( (vg.gl_context = SDL_GL_CreateContext(vg.window) )){
753 SDL_GL_GetDrawableSize( vg.window, &vg.window_x, &vg.window_y );
754 vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
755 }
756 else{
757 vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
758 SDL_Quit();
759 exit(0);
760 }
761
762 if( !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress) ) {
763 vg_error( "Glad Failed to initialize\n" );
764 SDL_GL_DeleteContext( vg.gl_context );
765 SDL_Quit();
766 exit(0);
767 }
768
769 const unsigned char* glver = glGetString( GL_VERSION );
770 vg_success( "Load setup complete, OpenGL version: %s\n", glver );
771
772 SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
773
774 SDL_DisplayMode dispmode;
775 if( !SDL_GetWindowDisplayMode( vg.window, &dispmode ) ){
776 if( dispmode.refresh_rate ){
777 vg.display_refresh_rate = dispmode.refresh_rate;
778 }
779 }
780
781 if( vg.display_refresh_rate < 25 || vg.display_refresh_rate > 300 ){
782 vg.display_refresh_rate = 60;
783 }
784
785 vg_info( "Display refresh rate: %d\n", dispmode.refresh_rate );
786
787 #ifdef _WIN32
788 vg.fps_limit = vg.display_refresh_rate;
789 #else
790 /* request vsync by default on linux to avoid screen tearing.
791 * this does have its own issues with compositing on X11. */
792 vg.fps_limit = 0;
793 #endif
794 }
795
796 VG_STATIC void _vg_terminate(void)
797 {
798 /* Shutdown */
799 _vg_console_write_persistent();
800
801 SDL_AtomicLock( &vg.sl_status );
802 vg.engine_status = k_engine_status_none;
803 SDL_AtomicUnlock( &vg.sl_status );
804
805 _vg_loader_free();
806
807 vg_success( "If you see this it means everything went.. \"well\".....\n" );
808
809 SDL_GL_DeleteContext( vg.gl_context );
810 SDL_Quit();
811 exit(0);
812 }
813
814 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
815 {
816 _vg_process_launch_opts_internal( argc, argv );
817
818 /* Systems init */
819 vg_alloc_quota();
820 _vg_console_init();
821
822 vg_console_reg_var( "fps_limit", &vg.fps_limit, k_var_dtype_i32, 0 );
823 _vg_init_window( window_name );
824
825 vg_async_init();
826
827 SDL_SetRelativeMouseMode(1);
828 vg.thread_id_main = SDL_GetThreadID(NULL);
829
830 /* Opengl-required systems */
831 _vg_ui_init();
832 _vg_loader_init();
833
834 vg.engine_status = k_engine_status_load_internal;
835
836 _vg_opengl_sync_init();
837 vg_loader_start( _vg_load_full );
838 _vg_gameloop();
839 _vg_terminate();
840 }
841
842 VG_STATIC void vg_fatal_error( const char *fmt, ... )
843 {
844 va_list args;
845 va_start( args, fmt );
846 _vg_log_write( stderr, KRED " fatal" KWHT "| " KRED, fmt, args );
847 va_end( args );
848
849 vg_print_backtrace();
850
851 SDL_AtomicLock( &vg.sl_status );
852 vg.engine_status = k_engine_status_crashed;
853 SDL_AtomicUnlock( &vg.sl_status );
854
855 if( vg_thread_purpose() == k_thread_purpose_loader ){
856 longjmp( vg.env_loader_exit, 1 );
857 }
858 else{
859 vg_error( "There is no jump to the error runner thing yet! bai bai\n" );
860 _vg_terminate();
861 }
862 }
863
864 #else /* VG_GAME */
865
866 #include "vg_log.h"
867 VG_STATIC void vg_fatal_error( const char *fmt, ... )
868 {
869 va_list args;
870 va_start( args, fmt );
871 _vg_log_write( stderr, KRED " fatal" KWHT "| " KRED, fmt, args );
872 va_end( args );
873 exit(0);
874 }
875
876 #endif /* VG_GAME */
877
878 /*
879 * Graphic cards will check these to force it to use the GPU
880 */
881 u32 NvOptimusEnablement = 0x00000001;
882 int AmdPowerXpressRequestHighPerformance = 1;
883
884 #endif /* VG_HEADER_H */