#ifndef VG_NON_CLIENT
-m4x4f vg_pv;
-
-int vg_window_x = 0;
-int vg_window_y = 0;
-int vg_samples = 0;
-
-v2f vg_mouse;
-v2f vg_mouse_wheel;
-v3f vg_mouse_ws;
-
-double vg_time,
- vg_time_last,
- vg_time_delta;
-
struct vg
{
/* Engine sync */
}
engine_status;
const char *str_const_engine_err;
-
int is_loaded;
+ /* Window information */
+ int window_x,
+ window_y,
+ samples;
+ float refresh_rate;
+
+ v2f mouse,
+ mouse_wheel;
+
+ /* Runtime */
+ double time,
+ time_last,
+ time_delta,
+ accumulator;
+
+ int fixed_iterations;
+
+ enum engine_stage
+ {
+ k_engine_stage_none,
+ k_engine_stage_update,
+ k_engine_stage_update_fixed,
+ k_engine_stage_rendering,
+ k_engine_stage_ui
+ }
+ engine_stage;
+
+ /* graphics */
+ m4x4f pv;
+
/* Gamepad */
GLFWgamepadstate gamepad;
int gamepad_ready;
#define VG_STRINGIT( X ) #X
#define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
+#include "vg_console.h"
+#include "vg_profiler.h"
#include "vg_audio.h"
#include "vg_shader.h"
#include "vg_tex.h"
#include "vg_input.h"
#include "vg_ui.h"
-#include "vg_console.h"
#include "vg_lines.h"
#include "vg_debug.h"
#include "vg_loader.h"
#include "vg_opt.h"
+/* Diagnostic */
+static struct vg_profile vg_prof_update = {.name="update()"},
+ vg_prof_render = {.name="render()"};
+
#define VG_GAMELOOP
static void vg_register(void) VG_GAMELOOP;
static void vg_start(void) VG_GAMELOOP;
+
static void vg_update(int loaded) VG_GAMELOOP;
+static void vg_update_fixed(int loaded) VG_GAMELOOP;
+static void vg_update_post(int loaded) VG_GAMELOOP;
+
static void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
static void vg_render(void) VG_GAMELOOP;
static void vg_ui(void) VG_GAMELOOP;
void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
{
- vg_mouse[0] = xpos;
- vg_mouse[1] = ypos;
+ vg.mouse[0] = xpos;
+ vg.mouse[1] = ypos;
}
void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
{
- vg_mouse_wheel[0] += xoffset;
- vg_mouse_wheel[1] += yoffset;
+ vg.mouse_wheel[0] += xoffset;
+ vg.mouse_wheel[1] += yoffset;
}
void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
return;
}
- vg_window_x = w;
- vg_window_y = h;
+ vg.window_x = w;
+ vg.window_y = h;
vg_framebuffer_resize(w,h);
}
vg_loader_highwater( vg_gamepad_init, NULL, NULL );
vg_loader_highwater( vg_lines_init, vg_lines_free, NULL );
vg_loader_highwater( vg_audio_init, vg_audio_free, NULL );
+ vg_loader_highwater( vg_profiler_init, NULL, NULL );
/* client */
vg_load();
{
if( (arg = vg_opt_arg( 'w' )) )
{
- vg_window_x = atoi( arg );
+ vg.window_x = atoi( arg );
}
if( (arg = vg_opt_arg( 'h' )) )
{
- vg_window_y = atoi( arg );
+ vg.window_y = atoi( arg );
}
if( (arg = vg_long_opt_arg( "samples" )) )
{
- vg_samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
+ vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
}
}
glfwWindowHint( GLFW_RESIZABLE, GLFW_FALSE );
glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
- glfwWindowHint( GLFW_SAMPLES, vg_samples );
+ glfwWindowHint( GLFW_SAMPLES, vg.samples );
GLFWmonitor *monitor_primary = glfwGetPrimaryMonitor();
glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
- /* TODO? */
- glfwWindowHint( GLFW_REFRESH_RATE, 60 );
+ glfwWindowHint( GLFW_REFRESH_RATE, mode->refreshRate );
- if( !vg_window_x )
- vg_window_x = mode->width;
+ if( !vg.window_x )
+ vg.window_x = mode->width;
- if( !vg_window_y )
- vg_window_y = mode->height;
+ if( !vg.window_y )
+ vg.window_y = mode->height;
+ vg.refresh_rate = mode->refreshRate;
- if( (vg.window = glfwCreateWindow( vg_window_x, vg_window_y,
+ if( (vg.window = glfwCreateWindow( vg.window_x, vg.window_y,
window_name, monitor_primary, NULL)) )
{
- glfwGetFramebufferSize( vg.window, &vg_window_x, &vg_window_y );
- vg_success( "Window created (%dx%d)\n", vg_window_x, vg_window_y );
+ glfwGetFramebufferSize( vg.window, &vg.window_x, &vg.window_y );
+ vg_success( "Window created (%dx%d)\n", vg.window_x, vg.window_y );
}
else
{
vg_loader_start();
int loaded = 0;
- int counter =0;
while(1)
{
if( glfwWindowShouldClose( vg.window ) )
break;
- v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
+ v2_copy( (v2f){ 0.0f, 0.0f }, vg.mouse_wheel );
glfwPollEvents();
- vg_time_last = vg_time;
- vg_time = glfwGetTime();
- vg_time_delta = vg_minf( vg_time - vg_time_last, 0.1f );
+ vg.time_last = vg.time;
+ vg.time = glfwGetTime();
+ vg.time_delta = vg.time-vg.time_last;
if( vg.is_loaded )
{
- glClearColor( 0.0f,sinf(vg_time*20.0)*0.5f+0.5f,0.0f,1.0f );
+ glClearColor( 0.0f,sinf(vg.time*20.0)*0.5f+0.5f,0.0f,1.0f );
glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
if( !loaded )
vg_loader_render();
}
+ /*
+ * Game logic
+ * -------------------------------------------------------
+ */
+ vg_profile_begin( &vg_prof_update );
vg_update_inputs();
+
+ vg.engine_stage = k_engine_stage_update;
vg_update( loaded );
- counter ++;
+ /* Fixed update loop */
+ vg.engine_stage = k_engine_stage_update_fixed;
+ vg.accumulator += vg.time_delta;
-#if 0
- if( counter == 30 )
- vg_fatal_exit_loop( "Test crash from main, while loading (-O0)" );
-#endif
+ vg.fixed_iterations = 0;
+ while( vg.accumulator >= (VG_TIMESTEP_FIXED-0.00125) )
+ {
+ vg_update_fixed( loaded );
+
+ vg.accumulator -= VG_TIMESTEP_FIXED;
+ vg.accumulator = VG_MAX( 0.0, vg.accumulator );
+
+ vg.fixed_iterations ++;
+ if( vg.fixed_iterations == 8 )
+ {
+ break;
+ }
+ }
+
+ /*
+ * Rendering
+ * ---------------------------------------------
+ */
+ vg.engine_stage = k_engine_stage_update;
+ vg_update_post( loaded );
+ vg_profile_end( &vg_prof_update );
+
+ vg_profile_begin( &vg_prof_render );
if( loaded )
{
+ /* render */
+ vg.engine_stage = k_engine_stage_rendering;
vg_render();
/* ui */
+ vg.engine_stage = k_engine_stage_ui;
{
- ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
- ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1],
+ ui_begin( &ui_global_ctx, vg.window_x, vg.window_y );
+ ui_set_mouse( &ui_global_ctx, vg.mouse[0], vg.mouse[1],
vg_get_button_state( "primary" ) );
- audio_debug_ui( vg_pv );
+ vg_profile_drawn(
+ (struct vg_profile *[]){&vg_prof_update,&vg_prof_render}, 2,
+ (1.0f/(float)vg.refresh_rate)*1000.0f,
+ (ui_rect){ 4, 4, 250, 0 }, 0
+ );
+
+ audio_debug_ui( vg.pv );
vg_ui();
vg_console_draw();
}
}
+ vg_profile_end( &vg_prof_render );
+
glfwSwapBuffers( vg.window );
vg_run_synced_content();
}
/*
* Immediately transfer away from calling thread into a safe loop, signal for
* others to shutdown, then free everything once the user closes the window.
+ *
+ * FIXME(bug): glfwWindowShouldClose() never returns 1 in windows via wine, only
+ * when calling the program from outside its normal directory.
+ *
+ * A workaround is placed to skip the error loop on windows builds
*/
static void vg_fatal_exit_loop( const char *error )
{
}
vg_audio_free(NULL);
- /*
- * todo: draw error loop
- */
+#ifndef _WIN32
while(1)
{
if( glfwWindowShouldClose( vg.window ) )
break;
glfwPollEvents();
+
+ glBindFramebuffer( GL_FRAMEBUFFER, 0 );
+ glEnable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
+ glBlendEquation(GL_FUNC_ADD);
glClearColor( 0.15f + sinf(glfwGetTime())*0.1f, 0.0f, 0.0f,1.0f );
glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
- glViewport( 0,0, vg_window_x, vg_window_y );
+ glViewport( 0,0, vg.window_x, vg.window_y );
vg_render_log();
glfwSwapBuffers( vg.window );
}
+#endif
/* Can now shutdown and EXIT */
vg_loader_free();
void *mem, *decode_mem;
u32 mem_current,
mem_total;
+ u32 samples_last;
/* synchro */
int sync_locked;
/* System queue, and access from thread 0 */
audio_entity entity_queue[SFX_MAX_SYSTEMS];
int queue_len;
-
- char performance_info[128],
- performance_sub0[64],
- performance_sub1[64];
-
int debug_ui;
v3f listener_pos,
listener_ears;
-
- double perf_ms_decode,
- perf_ms_mix;
-
- u32 perf_measurements;
}
vg_audio;
+static struct vg_profile
+ _vg_prof_audio_decode = {.mode = k_profile_mode_accum,
+ .name = "[T2] audio_decode()"},
+ _vg_prof_audio_mix = {.mode = k_profile_mode_accum,
+ .name = "[T2] audio_mix()"},
+ vg_prof_audio_decode,
+ vg_prof_audio_mix;
+
static void *audio_alloc( u32 size )
{
u32 new_current = vg_audio.mem_current + size;
static void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
{
- struct timespec time_start, time_end;
- clock_gettime( CLOCK_REALTIME, &time_start );
+ vg_profile_begin( &_vg_prof_audio_decode );
struct active_audio_player *aap = &vg_audio.active_players[id];
audio_entity *ent = &aap->ent;
}
ent->cur = cursor;
-
- clock_gettime( CLOCK_REALTIME, &time_end );
- vg_audio.perf_ms_decode += vg_time_diff( time_start, time_end );
+ vg_profile_end( &_vg_prof_audio_decode );
}
static void audio_entity_mix( aatree_ptr id, float *buffer,
audio_entity_get_samples( id, frame_count, pcf );
- struct timespec time_start, time_end;
- clock_gettime( CLOCK_REALTIME, &time_start );
+ vg_profile_begin( &_vg_prof_audio_mix );
if( ent->info.flags & AUDIO_FLAG_SPACIAL_3D )
audio_entity_spacialize( ent, &vol, &pan );
buffer_pos ++;
}
- clock_gettime( CLOCK_REALTIME, &time_end );
- vg_audio.perf_ms_mix += vg_time_diff( time_start, time_end );
+ vg_profile_end( &_vg_prof_audio_mix );
}
/*
audio_entity_mix( i, pOut32F, frame_count );
}
}
-
-#if 0
- vg_sleep_ms( 20 );
-#endif
/* redistribute */
audio_system_cleanup();
/* TODO: what the hell is this? */
(void)pInput;
- /*
- * Debug information
- */
- clock_gettime( CLOCK_REALTIME, &time_end );
-
- double elapsed = vg_time_diff( time_start, time_end ),
- budget = ((double)frame_count / 44100.0) * 1000.0,
- percent = (elapsed/budget) * 100.0;
-
- snprintf( vg_audio.performance_info, 127,
- "%.2fms/%.2fms (%.1f%%) (%u frames)",
- elapsed, budget, percent, frame_count );
- vg_audio.perf_measurements ++;
- if( vg_audio.perf_measurements >= 30 )
- {
- double ms_decode = vg_audio.perf_ms_decode * (1.0/30.0),
- ms_mix = vg_audio.perf_ms_mix * (1.0/30.0);
-
- snprintf( vg_audio.performance_sub0, 63, "Decode %.2fms", ms_decode );
- snprintf( vg_audio.performance_sub1, 63, "mix %.2fms", ms_mix );
+ audio_lock();
+ vg_profile_increment( &_vg_prof_audio_decode );
+ vg_profile_increment( &_vg_prof_audio_mix );
- vg_audio.perf_ms_decode = 0.0;
- vg_audio.perf_ms_mix = 0.0;
+ vg_prof_audio_mix = _vg_prof_audio_mix;
+ vg_prof_audio_decode = _vg_prof_audio_decode;
- vg_audio.perf_measurements = 0;
- }
+ vg_audio.samples_last = frame_count;
+ audio_unlock();
}
/* Decompress entire vorbis stream into buffer */
infos[ SFX_MAX_SYSTEMS ];
int num_systems = 0;
- char perf[128],
- psub0[64],
- psub1[64];
-
audio_lock();
for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
snd->vol = ent->info.vol*100.0f;
v3_copy( ent->info.world_position, snd->pos );
}
- strcpy( perf, vg_audio.performance_info );
- strcpy( psub0, vg_audio.performance_sub0 );
- strcpy( psub1, vg_audio.performance_sub1 );
+
+ float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
+ vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
+ &vg_prof_audio_mix }, 2,
+ budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
+ 250, 0 }, 3 );
audio_unlock();
+ char perf[128];
+
/* Draw UI */
- ui_global_ctx.cursor[0] = 10;
- ui_global_ctx.cursor[1] = 10;
+ ui_global_ctx.cursor[0] = 258;
+ ui_global_ctx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12;
ui_global_ctx.cursor[2] = 150;
ui_global_ctx.cursor[3] = 12;
-
- ui_text( &ui_global_ctx, ui_global_ctx.cursor, perf, 1, 0 );
- ui_global_ctx.cursor[1] += 20;
-
- ui_text( &ui_global_ctx, ui_global_ctx.cursor, psub0, 1, 0 );
- ui_global_ctx.cursor[1] += 20;
-
- ui_text( &ui_global_ctx, ui_global_ctx.cursor, psub1, 1, 0 );
- ui_global_ctx.cursor[1] += 20;
float usage = (float)vg_audio.mem_current / (1024.0f*1024.0f),
total = (float)vg_audio.mem_total / (1024.0f*1024.0f),
v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
ui_rect wr;
- wr[0] = wpos[0] * vg_window_x;
- wr[1] = (1.0f-wpos[1]) * vg_window_y;
+ wr[0] = wpos[0] * vg.window_x;
+ wr[1] = (1.0f-wpos[1]) * vg.window_y;
wr[2] = 100;
wr[3] = 17;
static inline float vg_get_axis( const char *axis );
static inline int vg_get_button( const char *button );
+
+/*
+ * Cannot be used in fixed update
+ */
static inline int vg_get_button_down( const char *button );
static inline int vg_get_button_up( const char *button );
static inline int vg_get_button_down( const char *button )
{
+ if( vg.engine_stage == k_engine_stage_update_fixed )
+ vg_fatal_exit_loop( "Cannot use that here\n" );
+
int cur, prev;
vg_get_button_states( button, &cur, &prev );
static inline int vg_get_button_up( const char *button )
{
+ if( vg.engine_stage == k_engine_stage_update_fixed )
+ vg_fatal_exit_loop( "Cannot use that here\n" );
+
int cur, prev;
vg_get_button_states( button, &cur, &prev );
static void vg_render_log(void)
{
- ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
-
+ ui_begin( &ui_global_ctx, vg.window_x, vg.window_y );
int const fh = 14;
- int lines_screen_max = ((vg_window_y/fh)-2),
+ int lines_screen_max = ((vg.window_y/fh)-2),
lines_max_draw = VG_MIN( lines_screen_max, vg_list_size(vg_log.buffer) ),
lines_to_draw = VG_MIN( lines_max_draw, vg_log.buffer_line_count );
static void vg_loader_render(void)
{
- glViewport( 0,0, vg_window_x, vg_window_y );
+ glViewport( 0,0, vg.window_x, vg.window_y );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
glUseProgram( _shader_loader.id );
- glUniform1f( glGetUniformLocation( _shader_loader.id, "uTime" ), vg_time );
+ glUniform1f( glGetUniformLocation( _shader_loader.id, "uTime" ), vg.time );
glBindVertexArray( vg_loader.vao );
glDrawArrays( GL_TRIANGLES, 0, 6 );
char buffer[512];
int i, j;
- for( i = 0; i < vg_list_size( buffer ); i ++ )
+ for( i=0; i<vg_list_size( buffer ); i ++ )
{
if( prefix[i] )
buffer[i] = prefix[i];
--- /dev/null
+#ifndef VG_PROFILER_H
+#define VG_PROFILER_H
+
+#include <alloca.h>
+#include "vg_platform.h"
+
+#define VG_PROFILE_SAMPLE_COUNT 128
+
+static int vg_profiler = 0;
+
+struct vg_profile
+{
+ const char *name;
+
+ float samples[ VG_PROFILE_SAMPLE_COUNT ];
+ u32 buffer_count, buffer_current;
+
+ enum profile_mode
+ {
+ k_profile_mode_frame,
+ k_profile_mode_accum
+ }
+ mode;
+
+ struct timespec start;
+};
+
+static void vg_profile_begin( struct vg_profile *profile )
+{
+ clock_gettime( CLOCK_REALTIME, &profile->start );
+}
+
+static void vg_profile_increment( struct vg_profile *profile )
+{
+ profile->buffer_current ++;
+
+ if( profile->buffer_count < VG_PROFILE_SAMPLE_COUNT )
+ profile->buffer_count ++;
+
+ if( profile->buffer_current >= VG_PROFILE_SAMPLE_COUNT )
+ profile->buffer_current = 0;
+
+ profile->samples[ profile->buffer_current ] = 0.0f;
+}
+
+static void vg_profile_end( struct vg_profile *profile )
+{
+ struct timespec time_end;
+
+ clock_gettime( CLOCK_REALTIME, &time_end );
+ float delta = vg_time_diff( profile->start, time_end );
+
+
+ if( profile->mode == k_profile_mode_frame )
+ {
+ profile->samples[ profile->buffer_current ] = delta;
+ vg_profile_increment( profile );
+ }
+ else
+ {
+ profile->samples[ profile->buffer_current ] += delta;
+ }
+}
+
+static void vg_profile_drawn( struct vg_profile **profiles, u32 count,
+ float budget, ui_rect panel, u32 colour_offset )
+{
+ if( !vg_profiler )
+ return;
+
+ if( panel[2] == 0 )
+ panel[2] = 256;
+
+ if( panel[3] == 0 )
+ panel[3] = VG_PROFILE_SAMPLE_COUNT * 2;
+
+ float sh = panel[3] / VG_PROFILE_SAMPLE_COUNT,
+ sw = panel[2];
+
+ float *avgs = alloca( count * sizeof(float) );
+ int *ptrs = alloca( count * sizeof(int) );
+
+ for( int i=0; i<count; i++ )
+ {
+ ptrs[i] = profiles[i]->buffer_current;
+ avgs[i] = 0.0f;
+ }
+
+ u32 colours[] = { 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000,
+ 0xffff00ff, 0xffffff00 };
+
+ for( int i=0; i<VG_PROFILE_SAMPLE_COUNT-1; i++ )
+ {
+ float total = 0.0f;
+
+ for( int j=0; j<count; j++ )
+ {
+ ptrs[j] --;
+
+ if( ptrs[j] < 0 )
+ ptrs[j] = VG_PROFILE_SAMPLE_COUNT-1;
+
+ float sample = profiles[j]->samples[ptrs[j]],
+ px = (total / (budget*0.25f)) * sw,
+ wx = (sample / (budget*0.25f)) * sw;
+
+ ui_rect block = { panel[0] + px, panel[1] + (float)i*sh,
+ wx, sh };
+
+ u32 colour = colours[ (j+colour_offset) % vg_list_size(colours) ];
+ ui_fill_rect( &ui_global_ctx, block, colour );
+
+ total += sample;
+ avgs[j] += sample;
+ }
+ }
+
+ char infbuf[64];
+
+ for( int i=0; i<count; i++ )
+ {
+ snprintf( infbuf, 64, "%.1fms %s",
+ avgs[i] * (1.0f/(VG_PROFILE_SAMPLE_COUNT-1)),
+ profiles[i]->name );
+
+ ui_text( &ui_global_ctx, (ui_rect){ panel[0] + panel[2] + 4,
+ panel[1] + i * 14, 0, 0 },
+ infbuf,
+ 1,
+ k_text_align_left );
+ }
+}
+
+static void vg_profiler_init(void)
+{
+ vg_convar_push( (struct vg_convar){
+ .name = "vg_profiler",
+ .data = &vg_profiler,
+ .data_type = k_convar_dtype_i32,
+ .opt_i32 = { .min=0, .max=1, .clamp=1 },
+ .persistent = 1
+ });
+}
+
+#endif /* VG_PROFILER_H */
view_override = view;
m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } );
- m3x3_scale( view, (v3f){ 1.0f/((float)vg_window_x*0.5f),
- -1.0f/((float)vg_window_y*0.5f), 1.0f } );
+ m3x3_scale( view, (v3f){ 1.0f/((float)vg.window_x*0.5f),
+ -1.0f/((float)vg.window_y*0.5f), 1.0f } );
}
/* TODO? */