simplify gitignore
[vg.git] / src / vg / vg.h
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
2
3 /*
4 * Memory model:
5 * [global (.data)] [temp-stack] [system-stack] [game-heap]
6 *
7 * 1. Program starts: .data memory is loaded
8 * 2. System initialization:
9 * 2a. the large heap buffer is allocated
10 * 2b. each engine system is initialized in order, using some of the
11 * system stack
12 * 2c. game systems are also put into here
13 */
14
15
16 #ifndef VG_HEADER_H
17 #define VG_HEADER_H
18
19 #include "vg_platform.h"
20 #include "vg_mem.h"
21
22 #ifndef _WIN32
23 #include <execinfo.h>
24 #endif
25
26
27 #if defined(VG_SERVER) || defined(VG_TOOLS)
28 #define VG_NON_CLIENT
29 #endif
30
31 #ifndef VG_SERVER
32 #include "../../dep/glad/glad.h"
33
34 #define GLFW_INCLUDE_GLCOREARB
35
36 #ifdef _WIN32
37 #define GLFW_DLL
38 #endif
39
40 #include "../../dep/glfw/glfw3.h"
41 #endif
42
43 #include "vg_stdint.h"
44
45 void vg_register_exit( void( *funcptr )(void), const char *name );
46
47 #include "vg_m.h"
48 #include "vg_io.h"
49 #include "vg_log.h"
50
51 #ifdef VG_STEAM
52 //#include "vg_steamworks.h"
53 #include "vg_steam.h"
54 #endif
55
56 #ifndef VG_NON_CLIENT
57
58 struct vg
59 {
60 /* Engine sync */
61 GLFWwindow* window;
62
63 vg_mutex mux_context;
64 vg_semaphore sem_allow_exec,
65 sem_exec_finished,
66 sem_loader,
67 sem_fatal;
68 int exec_context;
69
70 vg_mutex mux_engine_status;
71 enum engine_status
72 {
73 k_engine_status_none,
74 k_engine_status_running,
75 k_engine_status_crashed
76 }
77 engine_status;
78 const char *str_const_engine_err;
79 int is_loaded;
80
81 /* Window information */
82 int window_x,
83 window_y,
84 samples;
85 float refresh_rate;
86
87 double mouse_pos[2];
88 v2f
89 mouse_delta,
90 mouse_wheel;
91
92 /* Runtime */
93 double time,
94 time_delta,
95 frame_delta,
96 time_real,
97 time_real_last,
98 time_rate,
99 accumulator;
100
101 int fixed_iterations;
102
103 enum engine_stage
104 {
105 k_engine_stage_none,
106 k_engine_stage_update,
107 k_engine_stage_update_fixed,
108 k_engine_stage_rendering,
109 k_engine_stage_ui
110 }
111 engine_stage;
112
113 /* graphics */
114 m4x4f pv;
115 enum quality_profile
116 {
117 k_quality_profile_high = 0,
118 k_quality_profile_low = 1,
119 }
120 quality_profile;
121
122 /* Gamepad */
123 GLFWgamepadstate gamepad;
124 int gamepad_ready;
125 const char *gamepad_name;
126 int gamepad_id;
127 int gamepad_use_trackpad_look;
128 }
129 VG_STATIC vg = { .time_rate = 1.0 };
130
131 struct vg_thread_info
132 {
133 enum vg_thread_purpose
134 {
135 k_thread_purpose_nothing,
136 k_thread_purpose_main,
137 k_thread_purpose_loader
138 }
139 purpose;
140
141 int gl_context_level;
142 };
143
144 static VG_THREAD_LOCAL struct vg_thread_info vg_thread_info;
145
146
147 //#define VG_SYNC_DEBUG
148
149 #ifdef VG_SYNC_DEBUG
150 #define VG_SYNC_LOG(STR,...) vg_info(STR,vg_thread_info.purpose,##__VA_ARGS__)
151 #else
152 #define VG_SYNC_LOG(...)
153 #endif
154
155 VG_STATIC void vg_fatal_exit_loop( const char *error );
156 VG_STATIC void vg_required( void *ptr, const char *path )
157 {
158 if( !ptr )
159 {
160 vg_fatal_exit_loop( path );
161 }
162 }
163
164
165 VG_STATIC void vg_ensure_engine_running(void)
166 {
167 /* Check if the engine is no longer running */
168 vg_mutex_lock( &vg.mux_engine_status );
169 if( vg.engine_status != k_engine_status_running )
170 {
171 VG_SYNC_LOG( "[%d] Engine is no longer running\n");
172 vg_mutex_unlock( &vg.mux_engine_status );
173
174 /* Safe to disregard loader thread from this point on, elswhere */
175 if( vg_thread_info.purpose == k_thread_purpose_loader )
176 {
177 vg_semaphore_post( &vg.sem_loader );
178 }
179
180 VG_SYNC_LOG( "[%d] about to kill\n");
181 vg_thread_exit();
182 }
183 vg_mutex_unlock( &vg.mux_engine_status );
184 }
185
186 /*
187 * Sync execution so that the OpenGL context is switched onto this thread.
188 * Anything after this call will be in a valid context.
189 */
190 VG_STATIC void vg_acquire_thread_sync(void)
191 {
192 /* We dont want to do anything if this is the main thread */
193 if( vg_thread_info.purpose == k_thread_purpose_main )
194 return;
195
196 assert( vg_thread_info.purpose == k_thread_purpose_loader );
197
198 vg_ensure_engine_running();
199
200 /* Check if thread already has the context */
201 if( vg_thread_info.gl_context_level )
202 {
203 vg_thread_info.gl_context_level ++;
204 VG_SYNC_LOG( "[%d] We already have sync here\n" );
205 return;
206 }
207
208 vg_mutex_lock( &vg.mux_context );
209 VG_SYNC_LOG( "[%d] Signal to sync.\n" );
210 vg.exec_context = 1;
211 vg_mutex_unlock( &vg.mux_context );
212
213 /* wait until told we can go */
214 VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n" );
215 vg_semaphore_wait( &vg.sem_allow_exec );
216 glfwMakeContextCurrent( vg.window );
217
218 /* context now valid to work in while we hold up main thread */
219 VG_SYNC_LOG( "[%d] Context acquired.\n" );
220 vg_thread_info.gl_context_level ++;
221 }
222
223 /*
224 * Signify that we are done with the OpenGL context in this thread.
225 * Anything after this call will be in an undefined context.
226 */
227 VG_STATIC void vg_release_thread_sync(void)
228 {
229 if( vg_thread_info.purpose == k_thread_purpose_main )
230 return;
231
232 assert( vg_thread_info.purpose == k_thread_purpose_loader );
233
234 /* signal that we are done */
235 vg_thread_info.gl_context_level --;
236
237 if( !vg_thread_info.gl_context_level )
238 {
239 VG_SYNC_LOG( "[%d] Releasing context.\n" );
240 glfwMakeContextCurrent( NULL );
241 vg_semaphore_post( &vg.sem_exec_finished );
242 }
243 }
244
245 VG_STATIC void vg_run_synced_content(void)
246 {
247 assert( vg_thread_info.purpose == k_thread_purpose_main );
248
249 vg_mutex_lock( &vg.mux_context );
250
251 if( vg.exec_context != 0 )
252 {
253 VG_SYNC_LOG( "[%d] Allowing content (%d).\n", vg.exec_context );
254
255 /* allow operations to go */
256 vg_thread_info.gl_context_level = 0;
257 glfwMakeContextCurrent( NULL );
258 vg_semaphore_post( &vg.sem_allow_exec );
259
260 /* wait for operations to complete */
261 VG_SYNC_LOG( "[%d] Waiting for content (%d).\n", vg.exec_context );
262 vg_semaphore_wait( &vg.sem_exec_finished );
263
264 /* check if we killed the engine */
265 vg_ensure_engine_running();
266
267 /* re-engage main thread */
268 VG_SYNC_LOG( "[%d] Re-engaging.\n" );
269 vg.exec_context = 0;
270 glfwMakeContextCurrent( vg.window );
271 vg_thread_info.gl_context_level = 1;
272 }
273
274 vg_mutex_unlock( &vg.mux_context );
275 }
276
277 VG_STATIC void vg_opengl_sync_init(void)
278 {
279 vg_semaphore_init( &vg.sem_allow_exec, 0 );
280 vg_semaphore_init( &vg.sem_exec_finished, 0 );
281 vg_semaphore_init( &vg.sem_loader, 1 );
282 vg_semaphore_init( &vg.sem_fatal, 1 );
283 vg_mutex_init( &vg.mux_context );
284
285 vg_set_thread_name( "[vg] Main" );
286 vg_thread_info.purpose = k_thread_purpose_main;
287 vg_thread_info.gl_context_level = 1;
288 }
289
290 VG_STATIC void vg_checkgl( const char *src_info );
291 #define VG_STRINGIT( X ) #X
292 #define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
293
294 #include "vg_console.h"
295 #include "vg_profiler.h"
296 #include "vg_audio.h"
297 #include "vg_shader.h"
298 #include "vg_tex.h"
299 #include "vg_input.h"
300 #include "vg_ui.h"
301 #include "vg_lines.h"
302 #include "vg_loader.h"
303 #include "vg_opt.h"
304
305 /* Diagnostic */
306 VG_STATIC struct vg_profile vg_prof_update = {.name="update()"},
307 vg_prof_render = {.name="render()"};
308
309 #define VG_GAMELOOP
310 VG_STATIC void vg_register(void) VG_GAMELOOP;
311 VG_STATIC void vg_start(void) VG_GAMELOOP;
312
313 VG_STATIC void vg_update(int loaded) VG_GAMELOOP;
314 VG_STATIC void vg_update_fixed(int loaded) VG_GAMELOOP;
315 VG_STATIC void vg_update_post(int loaded) VG_GAMELOOP;
316
317 VG_STATIC void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
318 VG_STATIC void vg_render(void) VG_GAMELOOP;
319 VG_STATIC void vg_ui(void) VG_GAMELOOP;
320
321 VG_STATIC void vg_checkgl( const char *src_info )
322 {
323 int fail = 0;
324
325 GLenum err;
326 while( (err = glGetError()) != GL_NO_ERROR )
327 {
328 vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
329 fail = 1;
330 }
331
332 if( fail )
333 vg_fatal_exit_loop( "OpenGL Error" );
334 }
335
336 void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
337 {
338 vg.mouse_delta[0] += xpos - vg.mouse_pos[0];
339 vg.mouse_delta[1] += ypos - vg.mouse_pos[1];
340
341 vg.mouse_pos[0] = xpos;
342 vg.mouse_pos[1] = ypos;
343 }
344
345 void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
346 {
347 vg.mouse_wheel[0] += xoffset;
348 vg.mouse_wheel[1] += yoffset;
349 }
350
351 void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
352 {
353 if( !w || !h )
354 {
355 vg_warn( "Got a invalid framebuffer size: %dx%d... ignoring\n", w, h );
356 return;
357 }
358
359 vg.window_x = w;
360 vg.window_y = h;
361
362 vg_framebuffer_resize(w,h);
363 }
364
365 VG_STATIC void vg_bake_shaders(void)
366 {
367 vg_acquire_thread_sync();
368
369 #if 0
370 vg_function_push( (struct vg_cmd)
371 {
372 .name = "shaders",
373 .function = vg_shaders_live_recompile
374 });
375 #endif
376
377 vg_shaders_compile();
378 vg_release_thread_sync();
379 }
380
381 VG_STATIC void vg_preload(void);
382 VG_STATIC void vg_load(void);
383 VG_STATIC void vg_load_full(void)
384 {
385 vg_preload();
386
387 /* internal */
388 vg_loader_highwater( vg_gamepad_init, NULL, NULL );
389 vg_loader_highwater( vg_lines_init, NULL, NULL );
390 vg_loader_highwater( vg_audio_init, vg_audio_free, NULL );
391 vg_loader_highwater( vg_profiler_init, NULL, NULL );
392
393 /* client */
394 vg_load();
395
396 vg_acquire_thread_sync();
397 vg.is_loaded = 1;
398 vg_release_thread_sync();
399 }
400
401 VG_STATIC void vg_enter( int argc, char *argv[], const char *window_name )
402 {
403 char *arg;
404 while( vg_argp( argc, argv ) )
405 {
406 if( (arg = vg_opt_arg( 'w' )) )
407 {
408 vg.window_x = atoi( arg );
409 }
410
411 if( (arg = vg_opt_arg( 'h' )) )
412 {
413 vg.window_y = atoi( arg );
414 }
415
416 if( (arg = vg_long_opt_arg( "samples" )) )
417 {
418 vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
419 }
420
421 if( vg_long_opt( "use-libc-malloc" ) )
422 {
423 vg_mem.use_libc_malloc = atoi( arg );
424 }
425
426 if( vg_long_opt( "high-performance" ) )
427 {
428 vg.quality_profile = k_quality_profile_low;
429 }
430 }
431
432 vg_alloc_quota();
433 vg_log_init();
434 vg_console_init();
435
436 glfwInit();
437 glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
438 glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
439 glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
440 //glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_FALSE );
441 glfwWindowHint( GLFW_CONTEXT_RELEASE_BEHAVIOR, GLFW_RELEASE_BEHAVIOR_FLUSH );
442
443 glfwWindowHint( GLFW_RESIZABLE, GLFW_FALSE );
444 glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
445
446 glfwWindowHint( GLFW_SAMPLES, vg.samples );
447
448 GLFWmonitor *monitor_primary = glfwGetPrimaryMonitor();
449
450 const GLFWvidmode *mode = glfwGetVideoMode( monitor_primary );
451 glfwWindowHint( GLFW_RED_BITS, mode->redBits );
452 glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
453 glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
454
455 glfwWindowHint( GLFW_REFRESH_RATE, mode->refreshRate );
456
457 if( !vg.window_x )
458 vg.window_x = mode->width;
459
460 if( !vg.window_y )
461 vg.window_y = mode->height;
462
463 vg.refresh_rate = mode->refreshRate;
464
465 if( (vg.window = glfwCreateWindow( vg.window_x, vg.window_y,
466 window_name, monitor_primary, NULL)) )
467 {
468 glfwGetFramebufferSize( vg.window, &vg.window_x, &vg.window_y );
469 vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
470 }
471 else
472 {
473 vg_error( "GLFW Failed to initialize\n" );
474 return;
475 }
476
477 /* We need 3.1.2 for correct VSync on windows */
478 {
479 int vmaj, vmin, vrev;
480 glfwGetVersion( &vmaj, &vmin, &vrev );
481
482 if( vmaj < 3 ||
483 (vmaj == 3 && vmin < 1) ||
484 (vmaj == 3 && vmin == 1 && vrev < 2 ) )
485 {
486 vg_error( "GLFW out of date (%d.%d.%d); (3.1.2 is required)\n",
487 vmaj, vmin, vrev );
488
489 glfwTerminate();
490 return;
491 }
492
493 vg_success( "GLFW Version %d.%d.%d\n", vmaj, vmin, vrev );
494 }
495
496 glfwMakeContextCurrent( vg.window );
497 glfwSwapInterval( 1 );
498
499 glfwSetWindowSizeLimits( vg.window, 800, 600, GLFW_DONT_CARE,GLFW_DONT_CARE);
500 glfwSetFramebufferSizeCallback( vg.window, vg_framebuffer_resize_callback );
501
502 glfwSetCursorPosCallback( vg.window, vg_mouse_callback );
503 glfwSetScrollCallback( vg.window, vg_scroll_callback );
504
505 glfwSetCharCallback( vg.window, console_proc_wchar );
506 glfwSetKeyCallback( vg.window, console_proc_key );
507 glfwSetInputMode( vg.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED );
508
509 if( glfwRawMouseMotionSupported() )
510 glfwSetInputMode( vg.window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE );
511
512 if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) )
513 {
514 vg_error( "Glad Failed to initialize\n" );
515 glfwTerminate();
516 return;
517 }
518
519 const unsigned char* glver = glGetString( GL_VERSION );
520 vg_success( "Load setup complete, OpenGL version: %s\n", glver );
521
522 /* init systems
523 * -----------------------------------------------------------------------*/
524 ui_init_context();
525 vg_loader_init();
526
527 vg_mutex_init( &vg.mux_engine_status );
528 vg.engine_status = k_engine_status_running;
529
530 vg_opengl_sync_init();
531 vg_loader_start();
532
533 vg.accumulator = 0.75f * (1.0f/60.0f);
534
535 int loaded = 0;
536 while(1)
537 {
538 if( glfwWindowShouldClose( vg.window ) )
539 break;
540
541 v2_zero( vg.mouse_wheel );
542 v2_zero( vg.mouse_delta );
543
544 glfwPollEvents();
545
546 vg.time_real_last = vg.time_real;
547 vg.time_real = glfwGetTime();
548 vg.frame_delta = vg.time_real-vg.time_real_last;
549
550 /* scaled time */
551 vg.time_delta = vg.frame_delta * vg.time_rate;
552 vg.time += vg.time_delta;
553
554 if( vg.is_loaded )
555 {
556 if( !loaded )
557 {
558 vg_start();
559 loaded = 1;
560 }
561 }
562 else
563 {
564 vg_loader_render();
565 }
566
567 /*
568 * Game logic
569 * -------------------------------------------------------
570 */
571 vg_profile_begin( &vg_prof_update );
572 vg_update_inputs();
573
574 vg.engine_stage = k_engine_stage_update;
575 vg_update( loaded );
576
577 /* Fixed update loop */
578 vg.engine_stage = k_engine_stage_update_fixed;
579 vg.accumulator += vg.time_delta;
580
581 vg.fixed_iterations = 0;
582 vg_lines.allow_input = 1;
583 while( vg.accumulator >= (VG_TIMESTEP_FIXED-0.00125) )
584 {
585 vg_update_fixed( loaded );
586 vg_lines.allow_input = 0;
587
588 vg.accumulator -= VG_TIMESTEP_FIXED;
589 vg.accumulator = VG_MAX( 0.0, vg.accumulator );
590
591 vg.fixed_iterations ++;
592 if( vg.fixed_iterations == 8 )
593 {
594 break;
595 }
596 }
597 vg_lines.allow_input = 1;
598
599 /*
600 * Rendering
601 * ---------------------------------------------
602 */
603 vg.engine_stage = k_engine_stage_update;
604 vg_update_post( loaded );
605 vg_profile_end( &vg_prof_update );
606
607 vg_profile_begin( &vg_prof_render );
608
609 if( loaded )
610 {
611 /* render */
612 vg.engine_stage = k_engine_stage_rendering;
613 vg_render();
614
615 /* ui */
616 vg.engine_stage = k_engine_stage_ui;
617 {
618 ui_begin( vg.window_x, vg.window_y );
619
620 /* TODO */
621 ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
622
623 vg_profile_drawn(
624 (struct vg_profile *[]){&vg_prof_update,&vg_prof_render}, 2,
625 (1.0f/(float)vg.refresh_rate)*1000.0f,
626 (ui_rect){ 4, 4, 250, 0 }, 0
627 );
628
629 if( vg_profiler )
630 {
631
632 char perf[128];
633
634 snprintf( perf, 127,
635 "x: %d y: %d\n"
636 "refresh: %.1f (%.1fms)\n"
637 "samples: %d\n"
638 "iterations: %d (acc: %.3fms%%)\n",
639 vg.window_x, vg.window_y,
640 vg.refresh_rate, (1.0f/vg.refresh_rate)*1000.0f,
641 vg.samples,
642 vg.fixed_iterations,
643 (vg.accumulator/VG_TIMESTEP_FIXED)*100.0f );
644
645 ui_text( (ui_rect){258, 4+24+12,0,0},perf, 1,0);
646 }
647
648 audio_debug_ui( vg.pv );
649 vg_ui();
650 vg_console_draw();
651
652 ui_resolve();
653 ui_draw( NULL );
654 }
655 }
656
657 vg_profile_end( &vg_prof_render );
658
659 glfwSwapBuffers( vg.window );
660 vg_run_synced_content();
661 }
662
663 vg_console_write_persistent();
664
665 vg_mutex_lock( &vg.mux_engine_status );
666 vg.engine_status = k_engine_status_none;
667 vg_mutex_unlock( &vg.mux_engine_status );
668
669 vg_loader_free();
670
671 vg_success( "If you see this it means everything went.. \"well\".....\n" );
672 glfwTerminate();
673 }
674
675 /*
676 * Immediately transfer away from calling thread into a safe loop, signal for
677 * others to shutdown, then free everything once the user closes the window.
678 *
679 * FIXME(bug): glfwWindowShouldClose() never returns 1 in windows via wine, ONLY
680 * when calling the program from outside its normal directory.
681 */
682 VG_STATIC void vg_fatal_exit_loop( const char *error )
683 {
684 /*
685 * https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
686 * thanks gnu <3
687 *
688 * TODO: this on windows?
689 */
690
691 #ifndef _WIN32
692
693 void *array[20];
694 char **strings;
695 int size, i;
696
697 size = backtrace( array, 20 );
698 strings = backtrace_symbols( array, size );
699
700 if( strings != NULL )
701 {
702 vg_error( "---------------- gnu backtrace -------------\n" );
703
704 for( int i=0; i<size; i++ )
705 vg_info( "%s\n", strings[i] );
706
707 vg_error( "---------------- gnu backtrace -------------\n" );
708 }
709
710 free( strings );
711
712 #endif
713
714 vg_error( "Fatal error: %s\n", error );
715 assert( vg_semaphore_trywait( &vg.sem_fatal ) );
716
717 vg_mutex_lock( &vg.mux_engine_status );
718
719 if( vg.engine_status == k_engine_status_none )
720 {
721 vg_mutex_unlock( &vg.mux_engine_status );
722
723 /* TODO: Correct shutdown before other systems */
724 exit(0);
725 }
726 else
727 {
728 vg_mutex_unlock( &vg.mux_engine_status );
729
730 /*
731 * if main
732 * if loader running
733 * wait until loader checks in, it will die
734 * else
735 * pass immediately
736 * else
737 * if have context
738 * pass immediately
739 * else
740 * wait for main to get to us, it will never be used again
741 *
742 * undefined behaviour:
743 * fatal_exit_loop is called in both threads, preventing an appropriate
744 * reaction to the crash. This *should* be made
745 * obvious by the assertion
746 */
747 vg_acquire_thread_sync();
748
749 vg_mutex_lock( &vg.mux_engine_status );
750 vg.engine_status = k_engine_status_crashed;
751 vg.str_const_engine_err = error;
752 vg_mutex_unlock( &vg.mux_engine_status );
753
754 /*
755 * Wait for loader to finish what it was doing, if it was running.
756 * Then we can continue in our nice error screen
757 */
758 if( vg_thread_info.purpose == k_thread_purpose_main )
759 {
760 vg_semaphore_wait( &vg.sem_loader );
761 }
762 vg_audio_free(NULL);
763
764 while(1)
765 {
766 if( glfwWindowShouldClose( vg.window ) )
767 break;
768
769 if( glfwGetKey( vg.window, GLFW_KEY_ESCAPE ) )
770 break;
771
772 glfwPollEvents();
773
774 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
775 glEnable(GL_BLEND);
776 glDisable(GL_DEPTH_TEST);
777 glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
778 glBlendEquation(GL_FUNC_ADD);
779
780 glClearColor( 0.15f + sinf(glfwGetTime())*0.1f, 0.0f, 0.0f,1.0f );
781 glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
782 glViewport( 0,0, vg.window_x, vg.window_y );
783
784 vg_render_log();
785
786 glfwSwapBuffers( vg.window );
787 }
788
789 /* Can now shutdown and EXIT */
790 vg_loader_free();
791 glfwTerminate();
792 exit(0);
793 }
794 }
795
796 #else
797
798 VG_STATIC void vg_fatal_exit_loop( const char *error )
799 {
800 vg_error( "Fatal error: %s\n", error );
801 exit(0);
802 }
803
804 #endif
805
806 /*
807 * Graphic cards will check these to force it to use the GPU
808 */
809 u32 NvOptimusEnablement = 0x00000001;
810 int AmdPowerXpressRequestHighPerformance = 1;
811
812 #endif