Skip to content

Instantly share code, notes, and snippets.

@rygorous
Created December 9, 2013 01:28
Show Gist options
  • Save rygorous/7866167 to your computer and use it in GitHub Desktop.
Save rygorous/7866167 to your computer and use it in GitHub Desktop.
GLX
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