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