Created
December 9, 2013 01:28
-
-
Save rygorous/7866167 to your computer and use it in GitHub Desktop.
GLX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
GLuint glx_compile_shader_source( GLenum type, char const * source ) | |
{ | |
if ( !source ) | |
return 0; | |
// explicitly copy the source together so we can dump it as one string | |
size_t source_len = strlen( source ); | |
char * fused_source = new char[shader_header_len + source_len + 1]; | |
strcpy( fused_source, shader_header ); | |
strcpy( fused_source + shader_header_len, source ); | |
GLuint shader = glCreateShader( type ); | |
glShaderSource( shader, 1, &fused_source, 0 ); | |
glCompileShader( shader ); | |
GLint ok = 0; | |
glGetShaderiv( shader, GL_COMPILE_STATUS, &ok ); | |
if ( !ok ) | |
{ | |
GLint len = 0; | |
char * log; | |
glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &len ); | |
if ( !len || ( log = (char *)malloc( len ) ) == NULL ) | |
fprintf( stderr, "GL shader:\n----\n%s\n----\ncompilation failed, no info log.\n", fused_source ); | |
else | |
{ | |
glGetShaderInfoLog( shader, len, NULL, log ); | |
fprintf( stderr, "GL shader:\n----\n%s\n----\ncompilation failed:\n%s\n", fused_source, log ); | |
free( log ); | |
} | |
glDeleteShader( shader ); | |
shader = 0; | |
} | |
delete[] fused_source; | |
return shader; | |
} | |
GLuint glx_compile_shader_file( GLenum type, char const * filename ) | |
{ | |
char * source = read_file( filename ); | |
GLuint shader = glx_compile_shader_source( type, source ); | |
free( source ); | |
return shader; | |
} | |
static GLuint link_program( GLuint prog ) | |
{ | |
glLinkProgram( prog ); | |
GLint ok = 0; | |
glGetProgramiv( prog, GL_LINK_STATUS, &ok ); | |
if ( !ok ) | |
{ | |
GLint len = 0; | |
char * log; | |
glGetProgramiv( prog, GL_INFO_LOG_LENGTH, &len ); | |
if ( !len || ( log = (char *)malloc( len ) ) == NULL ) | |
fprintf( stderr, "GL program link failed, no info log.\n" ); | |
else | |
{ | |
glGetProgramInfoLog( prog, len, NULL, log ); | |
fprintf( stderr, "GL program link failed:\n%s\n", log ); | |
free( log ); | |
} | |
glDeleteProgram( prog ); | |
prog = 0; | |
} | |
return prog; | |
} | |
GLuint glx_simple_program( GLuint vert_shader, GLuint frag_shader, char const ** binds ) | |
{ | |
GLuint prog = glCreateProgram(); | |
glAttachShader( prog, vert_shader ); | |
glAttachShader( prog, frag_shader ); | |
if ( binds ) | |
{ | |
for ( GLuint i = 0 ; binds[i] ; i++ ) | |
glBindAttribLocation( prog, i, binds[i] ); | |
} | |
return link_program( prog ); | |
} | |
static GLuint compute_program( GLuint compute_shader ) | |
{ | |
if ( !compute_shader ) | |
return 0; | |
GLuint prog = glCreateProgram(); | |
glAttachShader( prog, compute_shader ); | |
prog = link_program( prog ); | |
if( prog ) | |
glDetachShader( prog, compute_shader ); | |
glDeleteShader( compute_shader ); | |
return prog; | |
} | |
GLuint glx_compute_program_source( char const * source ) | |
{ | |
return compute_program( glx_compile_shader_source( GL_COMPUTE_SHADER, source ) ); | |
} | |
GLuint glx_compute_program_file( char const * filename ) | |
{ | |
return compute_program( glx_compile_shader_file( GL_COMPUTE_SHADER, filename ) ); | |
} | |
GLuint glx_make_buffer( GLenum type, size_t size, void const * initial, GLenum usage ) | |
{ | |
GLuint id; | |
glGenBuffers( 1, &id ); | |
glBindBuffer( type, id ); | |
glBufferData( type, size, initial, usage ); | |
return id; | |
} | |
struct gl_texture_format { | |
GLenum internal; | |
GLenum fmt; | |
GLenum type; | |
int bpp; // bytes/pixel | |
}; | |
static gl_texture_format const formats[] = | |
{ | |
// one-channel | |
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE, 1 }, | |
{ GL_R8_SNORM, GL_RED, GL_BYTE, 1 }, | |
{ GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 1 }, | |
{ GL_R8I, GL_RED_INTEGER, GL_BYTE, 1 }, | |
{ GL_R16, GL_RED, GL_UNSIGNED_SHORT, 2 }, | |
{ GL_R16_SNORM, GL_RED, GL_SHORT, 2 }, | |
{ GL_R16F, GL_RED, GL_HALF_FLOAT, 2 }, | |
{ GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, 2 }, | |
{ GL_R16I, GL_RED_INTEGER, GL_SHORT, 2 }, | |
{ GL_R32F, GL_RED, GL_FLOAT, 4 }, | |
{ GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 4 }, | |
{ GL_R32I, GL_RED_INTEGER, GL_INT, 4 }, | |
// two-channel | |
{ GL_RG8, GL_RG, GL_UNSIGNED_BYTE, 2 }, | |
{ GL_RG8_SNORM, GL_RG, GL_BYTE, 2 }, | |
{ GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE, 2 }, | |
{ GL_RG8I, GL_RG_INTEGER, GL_BYTE, 2 }, | |
{ GL_RG16, GL_RG, GL_UNSIGNED_SHORT, 4 }, | |
{ GL_RG16_SNORM, GL_RG, GL_SHORT, 4 }, | |
{ GL_RG16F, GL_RG, GL_HALF_FLOAT, 4 }, | |
{ GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, 4 }, | |
{ GL_RG16I, GL_RG_INTEGER, GL_SHORT, 4 }, | |
{ GL_RG32F, GL_RG, GL_FLOAT, 8 }, | |
{ GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, 8 }, | |
{ GL_RG32I, GL_RG_INTEGER, GL_INT, 8 }, | |
// no three-channel formats yet! | |
// four-channel | |
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | |
{ GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | |
{ GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, 4 }, | |
{ GL_RGBA8UI, GL_RGBA, GL_UNSIGNED_BYTE, 4 }, | |
{ GL_RGBA8I, GL_RGBA, GL_BYTE, 4 }, | |
{ GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, 8 }, | |
{ GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, 8 }, | |
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, 8 }, | |
{ GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, 8 }, | |
{ GL_RGBA16I, GL_RGBA, GL_SHORT, 8 }, | |
{ GL_RGBA32F, GL_RGBA, GL_FLOAT, 16 }, | |
{ GL_RGBA32UI, GL_RGBA, GL_UNSIGNED_INT, 16 }, | |
{ GL_RGBA32I, GL_RGBA, GL_INT, 16 }, | |
}; | |
static gl_texture_format const * lookup_format( GLenum internalformat ) | |
{ | |
size_t i; | |
for ( i = 0 ; i < sizeof( formats ) / sizeof( *formats ) ; i++ ) | |
if ( formats[i].internal == internalformat ) | |
return &formats[i]; | |
return NULL; | |
} | |
void * glx_read_texture_level( GLuint tex, GLint level ) | |
{ | |
GLint w, h, fmt = 0; | |
glBindTexture( GL_TEXTURE_2D, tex ); | |
glGetTexLevelParameteriv( GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &w ); | |
glGetTexLevelParameteriv( GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &h ); | |
glGetTexLevelParameteriv( GL_TEXTURE_2D, level, GL_TEXTURE_INTERNAL_FORMAT, &fmt ); | |
gl_texture_format const * texfmt = lookup_format( fmt ); | |
if ( !texfmt ) | |
panic( "glx_read_texture_level: unsupported internal format 0x%04x\n", fmt ); | |
void * ret = malloc( w * h * texfmt->bpp ); | |
glPixelStorei( GL_PACK_ALIGNMENT, 1 ); | |
glGetTexImage( GL_TEXTURE_2D, level, texfmt->fmt, texfmt->type, ret ); | |
return ret; | |
} | |
static const size_t TIMER_SLOTS = 4; // depth of queue of in-flight queries (must be pow2) | |
struct glx_timer | |
{ | |
GLuint gl[TIMER_SLOTS]; | |
size_t issue_idx; // index of timer we're issuing | |
size_t retire_idx; // index of timer we're retiring | |
size_t warmup_frames; | |
run_stats * stats; | |
}; | |
static GLuint timer_get( glx_timer * timer, size_t index ) | |
{ | |
return timer->gl[ index & ( TIMER_SLOTS - 1 ) ]; | |
} | |
static void timer_ensure_max_in_flight( glx_timer * timer, size_t max_in_flight ) | |
{ | |
while ( ( timer->issue_idx - timer->retire_idx ) > max_in_flight ) | |
{ | |
// retire oldest timer in flight | |
GLuint handle = timer_get( timer, timer->retire_idx ); | |
GLint available = 0; | |
GLuint64 elapsed = 0; | |
while ( !available ) | |
glGetQueryObjectiv( handle, GL_QUERY_RESULT_AVAILABLE, &available ); | |
glGetQueryObjectui64v( handle, GL_QUERY_RESULT, &elapsed ); | |
if ( timer->retire_idx >= timer->warmup_frames ) | |
run_stats_record( timer->stats, (float) elapsed / 1e+6f ); // record ms not ns | |
timer->retire_idx++; | |
} | |
} | |
glx_timer * glx_timer_create( size_t warmup_frames ) | |
{ | |
glx_timer * timer = new glx_timer; | |
glGenQueries( TIMER_SLOTS, timer->gl ); | |
timer->issue_idx = 0; | |
timer->retire_idx = 0; | |
timer->warmup_frames = warmup_frames; | |
timer->stats = run_stats_create(); | |
return timer; | |
} | |
void glx_timer_destroy( glx_timer * timer ) | |
{ | |
if ( timer ) | |
{ | |
glDeleteQueries( TIMER_SLOTS, timer->gl ); | |
run_stats_destroy( timer->stats ); | |
delete timer; | |
} | |
} | |
void glx_timer_bracket_begin( glx_timer * timer ) | |
{ | |
// make sure we have a free timer to issue first | |
timer_ensure_max_in_flight( timer, TIMER_SLOTS - 1 ); | |
glBeginQuery( GL_TIME_ELAPSED, timer_get( timer, timer->issue_idx ) ); | |
timer->issue_idx++; | |
} | |
void glx_timer_bracket_end( glx_timer * timer ) | |
{ | |
glEndQuery( GL_TIME_ELAPSED ); | |
} | |
void glx_timer_report( glx_timer * timer, char const * label ) | |
{ | |
timer_ensure_max_in_flight( timer, 0 ); | |
run_stats_report( timer->stats, label ); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment