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