medium sized dollop
[vg.git] / src / vg / vg.h
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
2
3 #ifndef VG_HEADER_H
4 #define VG_HEADER_H
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <dirent.h>
9 #include <string.h>
10 #include <stdarg.h>
11 #include <ctype.h>
12 #include <math.h>
13
14 #if defined(VG_SERVER) || defined(VG_TOOLS)
15 #define VG_NON_CLIENT
16 #endif
17
18 #ifndef VG_SERVER
19 #include "../../dep/glad/glad.h"
20 #include "../../dep/glfw/glfw3.h"
21 #endif
22
23 #define STB_DS_IMPLEMENTATION
24 #include "stb/stb_ds.h"
25
26 #define QOI_IMPLEMENTATION
27 #include "phoboslab/qoi.h"
28
29 #include "vg_stdint.h"
30 #include "vg_platform.h"
31
32 void vg_register_exit( void( *funcptr )(void), const char *name );
33
34 #include "vg_m.h"
35 #include "vg_io.h"
36 #include "vg_log.h"
37
38 #ifdef VG_STEAM
39 //#include "vg_steamworks.h"
40 #include "vg_steam.h"
41 #endif
42
43 #ifndef VG_NON_CLIENT
44 #include "vg_gldiag.h"
45 #endif
46
47 #ifndef VG_NON_CLIENT
48
49 m4x4f vg_pv;
50
51 #ifdef VG_CAPTURE_MODE
52 int vg_window_x = 1920;
53 int vg_window_y = 1080;
54 #else
55 int vg_window_x = 1366;
56 int vg_window_y = 768;
57 #endif
58
59 v2f vg_mouse;
60 v2f vg_mouse_wheel;
61 v3f vg_mouse_ws;
62
63 double vg_time,
64 vg_time_last,
65 vg_time_delta;
66
67
68 struct vg
69 {
70 /* Engine sync */
71 GLFWwindow* window;
72
73 vg_mutex mux_context;
74 vg_semaphore sem_allow_exec,
75 sem_exec_finished;
76 int exec_context;
77
78 vg_mutex mux_engine_status;
79 int engine_running;
80
81
82 /* Gamepad */
83 GLFWgamepadstate gamepad;
84 int gamepad_ready;
85 const char *gamepad_name;
86 int gamepad_id;
87 }
88 static vg;
89
90
91 //#define VG_SYNC_DEBUG
92
93 #ifdef VG_SYNC_DEBUG
94 #define VG_SYNC_LOG(...) vg_info(__VA_ARGS__)
95 #else
96 #define VG_SYNC_LOG(...)
97 #endif
98
99 /*
100 * Sync execution so that the OpenGL context is switched onto this thread.
101 * Anything after this call will be in a valid context.
102 */
103 static int vg_acquire_thread_sync( int id )
104 {
105 if( id == 0 )
106 {
107 /* no action */
108 return 1;
109 }
110 else
111 {
112 /* Check if the engine is no longer running */
113 vg_mutex_lock( &vg.mux_engine_status );
114 if( !vg.engine_running )
115 {
116 VG_SYNC_LOG( "[%d] Engine is no longer running\n", id );
117 vg_mutex_unlock( &vg.mux_engine_status );
118 return 0;
119 }
120 vg_mutex_unlock( &vg.mux_engine_status );
121
122
123 vg_mutex_lock( &vg.mux_context );
124 VG_SYNC_LOG( "[%d] Signal to sync.\n", id );
125 vg.exec_context = id;
126 vg_mutex_unlock( &vg.mux_context );
127
128 /* wait until told we can go */
129 VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n", id );
130 vg_semaphore_wait( &vg.sem_allow_exec );
131 glfwMakeContextCurrent( vg.window );
132
133 /* context now valid to work in while we hold up main thread */
134 VG_SYNC_LOG( "[%d] Context acquired.\n", id );
135
136 return 1;
137 }
138 }
139
140 /*
141 * Signify that we are done with the OpenGL context in this thread.
142 * Anything after this call will be in an undefined context.
143 */
144 static void vg_release_thread_sync( int id )
145 {
146 if( id == 0 )
147 {
148 vg_mutex_lock( &vg.mux_context );
149
150 if( vg.exec_context != 0 )
151 {
152 VG_SYNC_LOG( "[%d] Allowing content (%d).\n", id, vg.exec_context );
153
154 /* allow operations to go */
155 glfwMakeContextCurrent( NULL );
156 vg_semaphore_post( &vg.sem_allow_exec );
157
158 /* wait for operations to complete */
159 VG_SYNC_LOG( "[%d] Waiting for content (%d).\n", id, vg.exec_context );
160 vg_semaphore_wait( &vg.sem_exec_finished );
161
162 /* re-engage main thread */
163 VG_SYNC_LOG( "[%d] Re-engaging.\n", id );
164 vg.exec_context = 0;
165 glfwMakeContextCurrent( vg.window );
166 }
167
168 vg_mutex_unlock( &vg.mux_context );
169 }
170 else
171 {
172 /* signal that we are done */
173 VG_SYNC_LOG( "[%d] Releasing context.\n", id );
174 glfwMakeContextCurrent( NULL );
175 vg_semaphore_post( &vg.sem_exec_finished );
176 }
177 }
178
179 static void vg_opengl_sync_init(void)
180 {
181 vg_semaphore_init( &vg.sem_allow_exec, 0 );
182 vg_semaphore_init( &vg.sem_exec_finished, 0 );
183 vg_mutex_init( &vg.mux_context );
184 }
185
186 static void vg_opengl_sync_free(void)
187 {
188 vg_semaphore_free( &vg.sem_allow_exec );
189 vg_semaphore_free( &vg.sem_exec_finished );
190 vg_mutex_free( &vg.mux_context );
191 }
192
193
194 int vg_checkgl( const char *src_info );
195 #define VG_STRINGIT( X ) #X
196 #define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
197
198 #include "vg_audio.h"
199 #include "vg_shader.h"
200 #include "vg_tex.h"
201 #include "vg_input.h"
202 #include "vg_ui.h"
203 #include "vg_console.h"
204 #include "vg_lines.h"
205 #include "vg_debug.h"
206 #include "vg_loader.h"
207
208 #define VG_GAMELOOP
209 static void vg_register(void) VG_GAMELOOP;
210 static void vg_start(void) VG_GAMELOOP;
211 static void vg_update(int loaded) VG_GAMELOOP;
212 static void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
213 static void vg_render(void) VG_GAMELOOP;
214 static void vg_ui(void) VG_GAMELOOP;
215
216 int vg_checkgl( const char *src_info )
217 {
218 int fail = 0;
219
220 GLenum err;
221 while( (err = glGetError()) != GL_NO_ERROR )
222 {
223 vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
224 fail = 1;
225 }
226
227 return fail;
228 }
229
230
231
232 void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
233 {
234 vg_mouse[0] = xpos;
235 vg_mouse[1] = ypos;
236 }
237
238 void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
239 {
240 vg_mouse_wheel[0] += xoffset;
241 vg_mouse_wheel[1] += yoffset;
242 }
243
244 void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
245 {
246 vg_window_x = w;
247 vg_window_y = h;
248
249 vg_framebuffer_resize(w,h);
250 }
251
252 static int vg_bake_shaders(void)
253 {
254 if( vg_acquire_thread_sync(1) )
255 {
256 if( !vg_shaders_recompile() )
257 {
258 vg_shaders_free(NULL);
259 vg_release_thread_sync(1);
260 return 0;
261 }
262 else
263 {
264 vg_release_thread_sync(1);
265
266 if( !vg_loader_highwater( vg_shaders_free, NULL ) ) return 0;
267 else return 1;
268 }
269 }
270
271 return 0;
272 }
273
274 int vg_preload(void);
275 int vg_load(void);
276 static int vg_load_full(void)
277 {
278 if( !vg_preload() ) return 0;
279
280 /* internal */
281 if( !vg_gamepad_init() ) return 0;
282 if( !vg_loader_highwater( NULL, NULL ) ) return 0;
283
284 if( !vg_lines_init() ) return 0;
285 if( !vg_loader_highwater( vg_lines_free, NULL ) ) return 0;
286
287 if( !vg_audio_init() ) return 0;
288 if( !vg_loader_highwater( vg_audio_free, NULL ) ) return 0;
289
290 /* client */
291 if( !vg_load() ) return 0;
292
293 return 1;
294 }
295
296 static void vg_enter( int argc, char *argv[], const char *window_name )
297 {
298 vg_log_init();
299 vg_console_init();
300
301 glfwInit();
302 glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
303 glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
304 glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
305 glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE );
306
307 glfwWindowHint( GLFW_RESIZABLE, GLFW_TRUE );
308 glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
309
310 #if 0
311 glfwWindowHint(GLFW_SAMPLES,4);
312 #endif
313
314 GLFWmonitor *monitor_primary = glfwGetPrimaryMonitor();
315
316 const GLFWvidmode *mode = glfwGetVideoMode( monitor_primary );
317 glfwWindowHint( GLFW_RED_BITS, mode->redBits );
318 glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
319 glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
320
321 /* This is set like this because of an OS issue */
322 int refresh_rate = mode->refreshRate;
323 if( refresh_rate < 28 || refresh_rate >= 144 )
324 refresh_rate = 60;
325 glfwWindowHint( GLFW_REFRESH_RATE, refresh_rate );
326
327 if( !(vg.window = glfwCreateWindow( vg_window_x, vg_window_y,
328 window_name, NULL, NULL)) )
329 {
330 vg_error( "GLFW Failed to initialize\n" );
331 return;
332 }
333
334 glfwMakeContextCurrent( vg.window );
335 glfwSwapInterval( 1 );
336
337 glfwSetWindowSizeLimits( vg.window, 800, 600, GLFW_DONT_CARE,GLFW_DONT_CARE);
338 glfwSetFramebufferSizeCallback( vg.window, vg_framebuffer_resize_callback );
339
340 glfwSetCursorPosCallback( vg.window, vg_mouse_callback );
341 glfwSetScrollCallback( vg.window, vg_scroll_callback );
342
343 glfwSetCharCallback( vg.window, console_proc_wchar );
344 glfwSetKeyCallback( vg.window, console_proc_key );
345 #if 0
346 glfwSetInputMode(vg_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
347 #endif
348
349 if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) )
350 {
351 vg_error( "Glad Failed to initialize\n" );
352 glfwTerminate();
353 return;
354 }
355
356 const unsigned char* glver = glGetString( GL_VERSION );
357 vg_success( "Load setup complete, OpenGL version: %s\n", glver );
358 vg_run_gfx_diagnostics();
359
360 if( !ui_default_init() )
361 goto il_exit_ui;
362
363 if( !vg_loader_init() )
364 goto il_exit_loader;
365
366 vg_mutex_init( &vg.mux_engine_status );
367 vg.engine_running = 1;
368
369 vg_opengl_sync_init();
370 vg_loader_start();
371
372 int loaded = 0;
373
374 while(1)
375 {
376 vg_acquire_thread_sync( 0 );
377
378 if( glfwWindowShouldClose( vg.window ) )
379 {
380 break;
381 }
382
383 v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
384 glfwPollEvents();
385
386 vg_time_last = vg_time;
387 vg_time = glfwGetTime();
388 vg_time_delta = vg_minf( vg_time - vg_time_last, 0.1f );
389
390 enum loader_status load_status = vg_loader_status();
391
392 if( load_status == k_loader_status_complete )
393 {
394 glClearColor( 0.0f,sinf(vg_time*20.0)*0.5f+0.5f,0.0f,1.0f );
395 glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
396
397 if( !loaded )
398 {
399 vg_start();
400 loaded = 1;
401 }
402 }
403 else
404 {
405 if( load_status == k_loader_status_fail )
406 {
407 break;
408 }
409 else
410 {
411 vg_loader_render();
412 }
413 }
414
415 vg_update_inputs();
416 vg_update( loaded );
417
418 if( loaded )
419 {
420 vg_render();
421
422 /* ui */
423 {
424 ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
425 ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1],
426 vg_get_button_state( "primary" ) );
427
428 audio_debug_ui( vg_pv );
429 vg_ui();
430 vg_console_draw();
431
432 ui_resolve( &ui_global_ctx );
433 ui_draw( &ui_global_ctx, NULL );
434 }
435 }
436
437
438
439
440 glfwSwapBuffers( vg.window );
441
442 if( VG_CHECK_GL_ERR() )
443 break;
444
445 vg_release_thread_sync( 0 );
446 }
447
448 vg_console_write_persistent();
449
450 vg_mutex_lock( &vg.mux_engine_status );
451 vg.engine_running = 0;
452 vg_mutex_unlock( &vg.mux_engine_status );
453
454
455 vg_loader_free();
456 vg_opengl_sync_free();
457
458 vg_mutex_free( &vg.mux_engine_status );
459
460 il_exit_loader:
461 ui_default_free();
462
463 il_exit_ui:
464 glfwTerminate();
465 }
466
467 #endif
468
469 /*
470 * Graphic cards will check these to force it to use the GPU
471 */
472 u32 NvOptimusEnablement = 0x00000001;
473 int AmdPowerXpressRequestHighPerformance = 1;
474
475 #endif