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