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