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