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