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