medfall

A super great game engine
Log | Files | Refs

renderer.cc (28325B)


      1 #include "glad.h"
      2 
      3 #include "intrinsics.h"
      4 #include "game.h"
      5 #include "gl.h"
      6 #include "renderer.h"
      7 #include "log.h"
      8 #include "linear_algebra.h"
      9 #include "blue_noise.h"
     10 #include "obj.h"
     11 
     12 STATIC_ASSERT( SAME_TYPE( VB, GLuint ) );
     13 STATIC_ASSERT( SAME_TYPE( IB, GLuint ) );
     14 STATIC_ASSERT( SAME_TYPE( Shader, GLuint ) );
     15 STATIC_ASSERT( SAME_TYPE( Texture, GLuint ) );
     16 STATIC_ASSERT( SAME_TYPE( TextureBufferObject, GLuint ) );
     17 STATIC_ASSERT( SAME_TYPE( FramebufferObject, GLuint ) );
     18 STATIC_ASSERT( SAME_TYPE( u32, GLuint ) );
     19 
     20 static const GLuint ATTR_POSITION = 0;
     21 static const GLuint ATTR_NORMAL = 1;
     22 static const GLuint ATTR_TEX_COORD0 = 2;
     23 static const GLuint ATTR_TEX_COORD1 = 3;
     24 static const GLuint ATTR_COLOUR = 4;
     25 static const GLuint ATTR_MODEL_TO_WORLD_COL0 = 5;
     26 
     27 static const u32 UNIFORM_BUFFER_SIZE = kilobytes( 32 );
     28 
     29 struct DrawCall {
     30 	RenderState render_state;
     31 	Mesh mesh;
     32 	u32 num_instances;
     33 	VB instance_data;
     34 };
     35 
     36 enum DeleteCommandType {
     37 	DELETE_VB,
     38 	DELETE_IB,
     39 	DELETE_TB,
     40 	DELETE_TEXTURE,
     41 	DELETE_MESH,
     42 };
     43 
     44 struct DeleteCommand {
     45 	DeleteCommandType type;
     46 	union {
     47 		VB vb;
     48 		IB ib;
     49 		TB tb;
     50 		FB fb;
     51 		Shader shader;
     52 		Texture texture;
     53 		Mesh mesh;
     54 	};
     55 };
     56 
     57 struct RenderPass {
     58 	size_t begin, one_past_end;
     59 	FB render_target;
     60 	GLbitfield clear_mask;
     61 };
     62 
     63 static DynamicArray< DrawCall > draw_calls;
     64 static DynamicArray< RenderPass > render_passes;
     65 static DynamicArray< DeleteCommand > deletes;
     66 
     67 static u32 draw_calls_this_frame;
     68 static u32 vertices_this_frame;
     69 
     70 static RenderPass current_render_pass;
     71 static bool in_frame;
     72 static bool in_pass;
     73 
     74 static GLuint uniforms;
     75 static size_t ubo_offset_alignment;
     76 static u8 * uniforms_buffer;
     77 static size_t uniforms_offset;
     78 
     79 static Texture blue_noise;
     80 
     81 static RenderState previous_render_state;
     82 static FramebufferObject previous_fbo;
     83 static u32 previous_viewport_width;
     84 static u32 previous_viewport_height;
     85 
     86 void renderer_init() {
     87 	glGenBuffers( 1, &uniforms );
     88 	glBindBuffer( GL_UNIFORM_BUFFER, uniforms );
     89 
     90 	in_frame = false;
     91 	in_pass = false;
     92 
     93 	previous_fbo = 0;
     94 	previous_viewport_width = 0;
     95 	previous_viewport_height = 0;
     96 
     97 	GLint alignment;
     98 	glGetIntegerv( GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment );
     99 	ubo_offset_alignment = checked_cast< size_t >( alignment );
    100 
    101 	blue_noise = load_png_memory( blue_noise_png, blue_noise_png_len, TEXFMT_R_U8NORM, 1 );
    102 }
    103 
    104 void renderer_term() {
    105 	glDeleteBuffers( 1, &uniforms );
    106 	renderer_delete_texture( blue_noise );
    107 }
    108 
    109 void renderer_begin_frame() {
    110 	ASSERT( !in_frame );
    111 	in_frame = true;
    112 
    113 	draw_calls.clear();
    114 	deletes.clear();
    115 	render_passes.clear();
    116 
    117 	draw_calls_this_frame = 0;
    118 	vertices_this_frame = 0;
    119 
    120 	glBufferData( GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW );
    121 	uniforms_buffer = ( u8 * ) glMapBuffer( GL_UNIFORM_BUFFER, GL_WRITE_ONLY );
    122 	ASSERT( uniforms_buffer != NULL );
    123 	uniforms_offset = 0;
    124 }
    125 
    126 static GLenum depthfunc_to_glenum( DepthFunc depth_func ) {
    127 	switch( depth_func ) {
    128 		case DEPTHFUNC_LESS: return GL_LESS;
    129 		case DEPTHFUNC_EQUAL: return GL_EQUAL;
    130 		case DEPTHFUNC_ALWAYS: return GL_ALWAYS;
    131 		case DEPTHFUNC_DISABLED: return GL_ALWAYS;
    132 	}
    133 
    134 	FATAL( "unsupported DepthFunc: {}", depth_func );
    135 	return GL_INVALID_ENUM;
    136 }
    137 
    138 static GLenum primitivetype_to_glenum( PrimitiveType primitive_type ) {
    139 	switch( primitive_type ) {
    140 		case PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES;
    141 		case PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
    142 		case PRIMITIVETYPE_POINTS: return GL_POINTS;
    143 		case PRIMITIVETYPE_LINES: return GL_LINES;
    144 	}
    145 
    146 	FATAL( "unsupported PrimitiveType: {}", primitive_type );
    147 	return GL_INVALID_ENUM;
    148 }
    149 
    150 static bool operator!=( UniformBinding a, UniformBinding b ) {
    151 	return a.offset != b.offset || a.size != b.size;
    152 }
    153 
    154 static void set_render_state( const RenderState & state ) {
    155 	if( state.shader != previous_render_state.shader ) {
    156 		glUseProgram( state.shader );
    157 	}
    158 
    159 	// uniforms
    160 	for( GLuint i = 0; i < UNIFORMS_COUNT; i++ ) {
    161 		if( state.uniforms[ i ] != previous_render_state.uniforms[ i ] ) {
    162 			UniformBinding binding = state.uniforms[ i ];
    163 			if( binding.size == 0 ) {
    164 				glBindBufferBase( GL_UNIFORM_BUFFER, i, 0 );
    165 			}
    166 			else {
    167 				glBindBufferRange( GL_UNIFORM_BUFFER, i, uniforms, binding.offset, binding.size );
    168 			}
    169 		}
    170 	}
    171 
    172 	// textures
    173 	for( GLuint i = 0; i < RENDERER_MAX_TEXTURES; i++ ) {
    174 		if( state.textures[ i ] != previous_render_state.textures[ i ] ) {
    175 			glActiveTexture( GL_TEXTURE0 + i );
    176 			glBindTexture( GL_TEXTURE_2D, state.textures[ i ] );
    177 		}
    178 	}
    179 
    180 	// texture buffers
    181 	for( GLuint i = 0; i < RENDERER_MAX_TEXTURE_BUFFERS; i++ ) {
    182 		if( state.tbs[ i ].texture != previous_render_state.tbs[ i ].texture ) {
    183 			glActiveTexture( GL_TEXTURE0 + i + RENDERER_MAX_TEXTURES );
    184 			glBindTexture( GL_TEXTURE_BUFFER, state.tbs[ i ].texture );
    185 		}
    186 	}
    187 
    188 	// depth writing
    189 	if( state.disable_depth_writes != previous_render_state.disable_depth_writes ) {
    190 		glDepthMask( state.disable_depth_writes ? GL_FALSE : GL_TRUE );
    191 	}
    192 
    193 	// backface culling
    194 	if( state.cull_face != previous_render_state.cull_face ) {
    195 		if( state.cull_face == CULLFACE_DISABLED ) {
    196 			glDisable( GL_CULL_FACE );
    197 		}
    198 		else {
    199 			if( previous_render_state.cull_face == CULLFACE_DISABLED ) {
    200 				glEnable( GL_CULL_FACE );
    201 			}
    202 			glCullFace( state.cull_face == CULLFACE_FRONT ? GL_FRONT : GL_BACK );
    203 		}
    204 	}
    205 
    206 	// depth testing
    207 	if( state.depth_func != previous_render_state.depth_func ) {
    208 		if( state.depth_func == DEPTHFUNC_DISABLED ) {
    209 			glDisable( GL_DEPTH_TEST );
    210 		}
    211 		else {
    212 			if( previous_render_state.depth_func == DEPTHFUNC_DISABLED ) {
    213 				glEnable( GL_DEPTH_TEST );
    214 			}
    215 			glDepthFunc( depthfunc_to_glenum( state.depth_func ) );
    216 		}
    217 	}
    218 
    219 	// alpha blending
    220 	if( state.enable_alpha_blending != previous_render_state.enable_alpha_blending ) {
    221 		if( state.enable_alpha_blending ) {
    222 			glEnable( GL_BLEND );
    223 			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    224 		}
    225 		else {
    226 			glDisable( GL_BLEND );
    227 		}
    228 	}
    229 
    230 	// polygon fill mode
    231 	if( state.wireframe != previous_render_state.wireframe ) {
    232 		if( state.wireframe ) {
    233 			glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    234 			glEnable( GL_POLYGON_OFFSET_LINE );
    235 			glPolygonOffset( -1, -1 );
    236 		}
    237 		else {
    238 			glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
    239 			glDisable( GL_POLYGON_OFFSET_LINE );
    240 		}
    241 	}
    242 
    243 	previous_render_state = state;
    244 }
    245 
    246 static void bind_fb( FB fb ) {
    247 	if( fb.fbo != previous_fbo ) {
    248 		glBindFramebuffer( GL_DRAW_FRAMEBUFFER, fb.fbo );
    249 		previous_fbo = fb.fbo;
    250 
    251 		u32 viewport_width, viewport_height;
    252 		if( fb.fbo == 0 ) {
    253 			v2u32 window_size = get_window_size();
    254 			viewport_width = window_size.x;
    255 			viewport_height = window_size.y;
    256 		}
    257 		else {
    258 			viewport_width = fb.width;
    259 			viewport_height = fb.height;
    260 		}
    261 
    262 		if( viewport_width != previous_viewport_width || viewport_height != previous_viewport_height ) {
    263 			previous_viewport_width = viewport_width;
    264 			previous_viewport_height = viewport_height;
    265 			glViewport( 0, 0, viewport_width, viewport_height );
    266 		}
    267 	}
    268 }
    269 
    270 static void drawcall_single( const Mesh & mesh, const RenderState & state ) {
    271 	set_render_state( state );
    272 
    273 	glBindVertexArray( mesh.vao );
    274 	GLenum primitive = primitivetype_to_glenum( mesh.primitive_type );
    275 	if( mesh.indices != 0 ) {
    276 		glDrawElements( primitive, mesh.num_vertices, GL_UNSIGNED_INT, 0 );
    277 	}
    278 	else {
    279 		glDrawArrays( primitive, 0, mesh.num_vertices );
    280 	}
    281 	glBindVertexArray( 0 );
    282 }
    283 
    284 static void drawcall_instanced( const Mesh & mesh, const RenderState & state, u32 num_instances, VB instance_data ) {
    285 	set_render_state( state );
    286 
    287 	glBindVertexArray( mesh.vao );
    288 	glBindBuffer( GL_ARRAY_BUFFER, instance_data );
    289 
    290 	for( int i = 0; i < 4; i++ ) {
    291 		GLuint loc = ATTR_MODEL_TO_WORLD_COL0 + i;
    292 		glEnableVertexAttribArray( loc );
    293 		glVertexAttribPointer( loc, 4, GL_FLOAT, GL_FALSE, sizeof( m4 ), ( GLvoid * ) ( i * sizeof( v4 ) ) );
    294 		glVertexAttribDivisor( loc, 1 );
    295 	}
    296 
    297 	GLenum primitive = primitivetype_to_glenum( mesh.primitive_type );
    298 	if( mesh.indices != 0 ) {
    299 		glDrawElementsInstanced( primitive, mesh.num_vertices, GL_UNSIGNED_INT, 0, checked_cast< s32 >( num_instances ) );
    300 	}
    301 	else {
    302 		glDrawArraysInstanced( primitive, 0, mesh.num_vertices, checked_cast< s32 >( num_instances ) );
    303 	}
    304 
    305 	glBindVertexArray( 0 );
    306 }
    307 
    308 void renderer_end_frame() {
    309 	ASSERT( !in_pass );
    310 	ASSERT( in_frame );
    311 	in_frame = false;
    312 
    313 	glUnmapBuffer( GL_UNIFORM_BUFFER );
    314 	uniforms_buffer = NULL;
    315 
    316 	for( const RenderPass & pass : render_passes ) {
    317 		bind_fb( pass.render_target );
    318 
    319 		if( pass.clear_mask != 0 ) {
    320 			glClear( pass.clear_mask );
    321 		}
    322 
    323 		// TODO: sort draw calls
    324 
    325 		for( const DrawCall & dc : draw_calls.slice( pass.begin, pass.one_past_end ) ) {
    326 			if( dc.num_instances == 0 ) {
    327 				drawcall_single( dc.mesh, dc.render_state );
    328 			}
    329 			else {
    330 				drawcall_instanced( dc.mesh, dc.render_state, dc.num_instances, dc.instance_data );
    331 			}
    332 		}
    333 	}
    334 
    335 	for( const DeleteCommand & del : deletes ) {
    336 		switch( del.type ) {
    337 			case DELETE_VB:
    338 				glDeleteBuffers( 1, &del.vb );
    339 				break;
    340 
    341 			case DELETE_IB:
    342 				glDeleteBuffers( 1, &del.ib );
    343 				break;
    344 
    345 			case DELETE_TB:
    346 				glDeleteBuffers( 1, &del.tb.tbo );
    347 				glDeleteTextures( 1, &del.tb.texture );
    348 				break;
    349 
    350 			case DELETE_TEXTURE:
    351 				glDeleteTextures( 1, &del.texture );
    352 				break;
    353 
    354 			case DELETE_MESH:
    355 				if( del.mesh.positions != 0 )
    356 					renderer_delete_vb( del.mesh.positions );
    357 				if( del.mesh.normals != 0 )
    358 					renderer_delete_vb( del.mesh.normals );
    359 				if( del.mesh.tex_coords0 != 0 )
    360 					renderer_delete_vb( del.mesh.tex_coords0 );
    361 				if( del.mesh.tex_coords1 != 0 )
    362 					renderer_delete_vb( del.mesh.tex_coords1 );
    363 				if( del.mesh.colours != 0 )
    364 					renderer_delete_vb( del.mesh.colours );
    365 				if( del.mesh.indices != 0 )
    366 					renderer_delete_ib( del.mesh.indices );
    367 
    368 				glDeleteVertexArrays( 1, &del.mesh.vao );
    369 				break;
    370 		}
    371 	}
    372 }
    373 
    374 void renderer_begin_pass( FB render_target, ClearColourBool clear_colour, ClearDepthBool clear_depth ) {
    375 	ASSERT( !in_pass );
    376 	in_pass = true;
    377 
    378 	GLbitfield clear_mask = 0;
    379 	if( clear_colour == RENDERER_CLEAR_COLOUR_DO ) {
    380 		clear_mask |= GL_COLOR_BUFFER_BIT;
    381 	}
    382 	if( clear_depth == RENDERER_CLEAR_DEPTH_DO ) {
    383 		clear_mask |= GL_DEPTH_BUFFER_BIT;
    384 	}
    385 
    386 	current_render_pass.begin = draw_calls.size();
    387 	current_render_pass.render_target = render_target;
    388 	current_render_pass.clear_mask = clear_mask;
    389 }
    390 
    391 void renderer_begin_pass( ClearColourBool clear_colour, ClearDepthBool clear_depth ) {
    392 	FB render_target = { };
    393 	renderer_begin_pass( render_target, clear_colour, clear_depth );
    394 }
    395 
    396 void renderer_end_pass() {
    397 	ASSERT( in_pass );
    398 	in_pass = false;
    399 
    400 	current_render_pass.one_past_end = draw_calls.size();
    401 	render_passes.append( current_render_pass );
    402 }
    403 
    404 u32 renderer_num_draw_calls() {
    405 	return draw_calls_this_frame;
    406 }
    407 
    408 u32 renderer_num_vertices() {
    409 	return vertices_this_frame;
    410 }
    411 
    412 Texture renderer_blue_noise() {
    413 	return blue_noise;
    414 }
    415 
    416 UniformBinding renderer_upload_uniforms( const void * data, size_t size, size_t alignment ) {
    417 	alignment = max( alignment, ubo_offset_alignment );
    418 
    419 	UniformBinding binding;
    420 	binding.offset = checked_cast< u32 >( align_power_of_2( uniforms_offset, alignment ) );
    421 	binding.size = checked_cast< u32 >( size );
    422 
    423 	size_t new_uniforms_offset = binding.offset + binding.size;
    424 	ASSERT( new_uniforms_offset < UNIFORM_BUFFER_SIZE );
    425 
    426 	// memset so we don't leave any gaps. good for write combined memory!
    427 	memset( uniforms_buffer + uniforms_offset, 0, binding.offset - uniforms_offset );
    428 	memcpy( uniforms_buffer + binding.offset, data, size );
    429 
    430 	uniforms_offset = new_uniforms_offset;
    431 
    432 	return binding;
    433 }
    434 
    435 static GLenum bufferusage_to_glenum( BufferUsage usage ) {
    436 	switch( usage ) {
    437 		case BUFFERUSAGE_STATIC: return GL_STATIC_DRAW;
    438 		case BUFFERUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW;
    439 		case BUFFERUSAGE_STREAM: return GL_STREAM_DRAW;
    440 	}
    441 
    442 	FATAL( "unsupported BufferUsage: {}", usage );
    443 	return GL_INVALID_ENUM;
    444 }
    445 
    446 static GLenum texfmt_to_internal_format( TextureFormat format ) {
    447 	switch( format ) {
    448 		case TEXFMT_INVALID:
    449 			break;
    450 
    451 		case TEXFMT_R_U8:
    452 			return GL_R8;
    453 		case TEXFMT_R_U8NORM:
    454 			return GL_R8_SNORM;
    455 		case TEXFMT_R_U16:
    456 			return GL_R16;
    457 		case TEXFMT_R_FLOAT:
    458 			return GL_R32F;
    459 
    460 		case TEXFMT_RGB_U8:
    461 			return GL_RGB8;
    462 		case TEXFMT_RGB_U8_SRGB:
    463 			return GL_SRGB8;
    464 		case TEXFMT_RGB_FLOAT:
    465 			return GL_RGB32F;
    466 
    467 		case TEXFMT_RGBA_U8:
    468 			return GL_RGBA8;
    469 		case TEXFMT_RGBA_U8_SRGB:
    470 			return GL_SRGB8_ALPHA8;
    471 		case TEXFMT_RGBA_FLOAT:
    472 			return GL_RGBA32F;
    473 
    474 		case TEXFMT_DEPTH:
    475 			return GL_DEPTH_COMPONENT24;
    476 
    477 		case TEXFMT_BC1:
    478 			return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
    479 		case TEXFMT_BC1_SRGB:
    480 			return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
    481 		case TEXFMT_BC4:
    482 			return GL_COMPRESSED_RED_RGTC1;
    483 		case TEXFMT_BC5:
    484 			return GL_COMPRESSED_RG_RGTC2;
    485 	}
    486 
    487 	FATAL( "unsupported TextureFormat: {}", format );
    488 	return GL_INVALID_ENUM;
    489 }
    490 
    491 static GLenum texturewrap_to_glenum( TextureWrapMode mode ) {
    492 	switch( mode ) {
    493 		case TEXWRAP_REPEAT:
    494 			return GL_REPEAT;
    495 		case TEXWRAP_CLAMP:
    496 			return GL_CLAMP_TO_EDGE;
    497 		case TEXWRAP_MIRROR:
    498 			return GL_MIRRORED_REPEAT;
    499 		case TEXWRAP_BORDER:
    500 			return GL_CLAMP_TO_BORDER;
    501 	}
    502 
    503 	FATAL( "unsupported TextureWrapMode: {}", mode );
    504 	return GL_INVALID_ENUM;
    505 }
    506 
    507 static void texturefilter_to_glenum( TextureFilterMode mode, bool mipmaps, GLenum * min_filter, GLenum * mag_filter ) {
    508 	switch( mode ) {
    509 		case TEXFILTER_LINEAR:
    510 			*min_filter = mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
    511 			*mag_filter = GL_LINEAR;
    512 			return;
    513 		case TEXFILTER_POINT:
    514 			*min_filter = mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST;
    515 			*mag_filter = GL_NEAREST;
    516 			return;
    517 	}
    518 
    519 	FATAL( "unsupported TextureFilterMode: {}", mode );
    520 }
    521 
    522 static bool is_compressed( TextureFormat format ) {
    523 	return format == TEXFMT_BC1 || format == TEXFMT_BC4 || format == TEXFMT_BC5;
    524 }
    525 
    526 VB renderer_new_vb( const void * data, u32 len, BufferUsage usage ) {
    527 	// glBufferData's length parameter is GLsizeiptr, so we need to make
    528 	// sure len fits in a signed 32bit int
    529 	ASSERT( len < S32_MAX );
    530 
    531 	GLuint vbo;
    532 	glGenBuffers( 1, &vbo );
    533 
    534 	if( data != NULL ) {
    535 		glBindBuffer( GL_ARRAY_BUFFER, vbo );
    536 		glBufferData( GL_ARRAY_BUFFER, len, data, bufferusage_to_glenum( usage ) );
    537 	}
    538 
    539 	return vbo;
    540 }
    541 
    542 void renderer_delete_vb( VB vb ) {
    543 	glDeleteBuffers( 1, &vb );
    544 }
    545 
    546 IB renderer_new_ib( const void * data, u32 len, BufferUsage usage ) {
    547 	ASSERT( len < S32_MAX );
    548 
    549 	GLuint ebo;
    550 	glGenBuffers( 1, &ebo );
    551 
    552 	if( data != NULL ) {
    553 		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ebo );
    554 		glBufferData( GL_ELEMENT_ARRAY_BUFFER, len, data, bufferusage_to_glenum( usage ) );
    555 	}
    556 
    557 	return ebo;
    558 }
    559 
    560 void renderer_delete_ib( IB ib ) {
    561 	glDeleteBuffers( 1, &ib );
    562 }
    563 
    564 TB renderer_new_tb( TextureFormat format, const void * data, u32 len, BufferUsage usage ) {
    565 	ASSERT( len < S32_MAX );
    566 	ASSERT( !is_compressed( format ) );
    567 
    568 	GLuint tbo, texture;
    569 	glGenBuffers( 1, &tbo );
    570 	glGenTextures( 1, &texture );
    571 
    572 	glBindBuffer( GL_TEXTURE_BUFFER, tbo );
    573 	glBindTexture( GL_TEXTURE_BUFFER, texture );
    574 	glTexBuffer( GL_TEXTURE_BUFFER, texfmt_to_internal_format( format ), tbo );
    575 
    576 	if( data != NULL ) {
    577 		glBufferData( GL_TEXTURE_BUFFER, len, data, bufferusage_to_glenum( usage ) );
    578 	}
    579 
    580 	TB tb = { };
    581 	tb.tbo = tbo;
    582 	tb.texture = texture;
    583 	return tb;
    584 }
    585 
    586 void renderer_tb_data( TB tb, const void * data, u32 len, BufferUsage usage ) {
    587 	ASSERT( len < S32_MAX );
    588 
    589 	glBindBuffer( GL_TEXTURE_BUFFER, tb.tbo );
    590 	glBufferData( GL_TEXTURE_BUFFER, len, NULL, bufferusage_to_glenum( usage ) );
    591 	glBufferData( GL_TEXTURE_BUFFER, len, data, bufferusage_to_glenum( usage ) );
    592 }
    593 
    594 void renderer_delete_tb( TB tb ) {
    595 	glDeleteBuffers( 1, &tb.tbo );
    596 	glDeleteTextures( 1, &tb.texture );
    597 }
    598 
    599 static GLenum framebufferattachment_to_glenum( FramebufferAttachment attachment ) {
    600 	switch( attachment ) {
    601 		case FB_COLOUR: return GL_COLOR_ATTACHMENT0;
    602 		case FB_DEPTH: return GL_DEPTH_ATTACHMENT;
    603 	}
    604 
    605 	FATAL( "invalid FramebufferAttachment: {}", attachment );
    606 	return GL_INVALID_ENUM;
    607 }
    608 
    609 FB renderer_new_fb( TextureConfig texture_config, FramebufferAttachment attachment ) {
    610 	ASSERT( !is_compressed( texture_config.format ) );
    611 
    612 	Texture texture = renderer_new_texture( texture_config );
    613 	GLuint fbo;
    614 
    615 	glGenFramebuffers( 1, &fbo );
    616 	glBindFramebuffer( GL_FRAMEBUFFER, fbo );
    617 	glFramebufferTexture2D( GL_FRAMEBUFFER, framebufferattachment_to_glenum( attachment ), GL_TEXTURE_2D, texture, 0 );
    618 	glReadBuffer( GL_NONE );
    619 
    620 	if( attachment == FB_DEPTH ) {
    621 		glDrawBuffer( GL_NONE );
    622 	}
    623 
    624 	ASSERT( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE );
    625 	glBindFramebuffer( GL_FRAMEBUFFER, 0 );
    626 
    627 	FB fb;
    628 	fb.fbo = fbo;
    629 	fb.attachment = attachment;
    630 	fb.texture = texture;
    631 	fb.width = texture_config.width;
    632 	fb.height = texture_config.height;
    633 	return fb;
    634 }
    635 
    636 void renderer_delete_fb( FB fb ) {
    637 	glDeleteBuffers( 1, &fb.fbo );
    638 	glDeleteTextures( 1, &fb.texture );
    639 }
    640 
    641 static GLuint new_gl_shader( GLenum type, array< const char * > srcs ) {
    642 	StaticArray< const char *, 16 > full_srcs;
    643 	full_srcs[ 0 ] = "#version 330\n";
    644 	full_srcs[ 1 ] = type == GL_VERTEX_SHADER ? "#define VERTEX_SHADER 1\n" : "#define FRAGMENT_SHADER 1\n";
    645 	GLsizei n = 2;
    646 	for( size_t i = 0; i < srcs.n; i++ ) {
    647 		if( srcs[ i ] != NULL ) {
    648 			full_srcs[ n ] = srcs[ i ];
    649 			n++;
    650 		}
    651 	}
    652 
    653 	GLuint shader = glCreateShader( type );
    654 	glShaderSource( shader, n, full_srcs.ptr(), NULL );
    655 	glCompileShader( shader );
    656 
    657 	GLint status;
    658 	glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
    659 
    660 	if( status == GL_FALSE ) {
    661 		char buf[ 1024 ];
    662 		glGetShaderInfoLog( shader, sizeof( buf ), NULL, buf );
    663 		WARN( "shader compilation failed: {}", buf );
    664 
    665 		// static char src[ 65536 ];
    666 		// glGetShaderSource( shader, sizeof( src ), NULL, src );
    667 		// printf( "%s\n", src );
    668 
    669 		return INVALID_SHADER;
    670 	}
    671 
    672 	return shader;
    673 }
    674 
    675 Shader renderer_new_shader( ShaderConfig config ) {
    676 	GLuint vs = new_gl_shader( GL_VERTEX_SHADER, config.srcs );
    677 	GLuint fs = new_gl_shader( GL_FRAGMENT_SHADER, config.srcs );
    678 
    679 	if( vs == 0 || fs == 0 ) {
    680 		if( vs != 0 ) glDeleteShader( vs );
    681 		if( fs != 0 ) glDeleteShader( fs );
    682 		return INVALID_SHADER;
    683 	}
    684 
    685 	GLuint program = glCreateProgram();
    686 	glAttachShader( program, vs );
    687 	glAttachShader( program, fs );
    688 
    689 	glBindAttribLocation( program, ATTR_POSITION, "position" );
    690 	glBindAttribLocation( program, ATTR_NORMAL, "normal" );
    691 	glBindAttribLocation( program, ATTR_TEX_COORD0, "tex_coord0" );
    692 	glBindAttribLocation( program, ATTR_TEX_COORD1, "tex_coord1" );
    693 	glBindAttribLocation( program, ATTR_COLOUR, "colour" );
    694 	glBindAttribLocation( program, ATTR_MODEL_TO_WORLD_COL0, "model_to_world" );
    695 
    696 	glLinkProgram( program );
    697 
    698 	glDeleteShader( vs );
    699 	glDeleteShader( fs );
    700 
    701 	GLint status;
    702 	glGetProgramiv( program, GL_LINK_STATUS, &status );
    703 	if( status == GL_FALSE ) {
    704 		char buf[ 1024 ];
    705 		glGetProgramInfoLog( program, sizeof( buf ), NULL, buf );
    706 		WARN( "shader linking failed: {}", buf );
    707 
    708 		return INVALID_SHADER;
    709 	}
    710 
    711 	const char * ubo_names[] = { "view", "model", "light_view", "window", "sun", "sky", "clipmap", "clipmap_skirt" };
    712 	STATIC_ASSERT( ARRAY_COUNT( ubo_names ) == UNIFORMS_COUNT );
    713 	for( GLuint i = 0; i < ARRAY_COUNT( ubo_names ); i++ ) {
    714 		GLuint idx = glGetUniformBlockIndex( program, ubo_names[ i ] );
    715 		if( idx != GL_INVALID_INDEX ) {
    716 			glUniformBlockBinding( program, idx, i );
    717 		}
    718 	}
    719 
    720 	glUseProgram( program );
    721 	for( GLint i = 0; i < RENDERER_MAX_TEXTURES; i++ ) {
    722 		if( config.texture_uniform_names[ i ] != NULL ) {
    723 			GLuint uniform = glGetUniformLocation( program, config.texture_uniform_names[ i ] );
    724 			if( uniform == GLuint( -1 ) ) {
    725 				WARN( "couldn't find texture uniform: {}", config.texture_uniform_names[ i ] );
    726 				glDeleteProgram( program );
    727 				return INVALID_SHADER;
    728 			}
    729 			glUniform1i( uniform, i );
    730 		}
    731 	}
    732 
    733 	for( GLint i = 0; i < RENDERER_MAX_TEXTURE_BUFFERS; i++ ) {
    734 		if( config.texture_buffer_uniform_names[ i ] != NULL ) {
    735 			GLuint uniform = glGetUniformLocation( program, config.texture_buffer_uniform_names[ i ] );
    736 			if( uniform == GLuint( -1 ) ) {
    737 				WARN( "couldn't find texturebuffer uniform: {}", config.texture_buffer_uniform_names[ i ] );
    738 				glDeleteProgram( program );
    739 				return INVALID_SHADER;
    740 			}
    741 			glUniform1i( uniform, i + RENDERER_MAX_TEXTURES );
    742 		}
    743 	}
    744 	glUseProgram( 0 );
    745 
    746 	return program;
    747 }
    748 
    749 Shader renderer_new_shader( const char * src ) {
    750 	ShaderConfig config;
    751 	config.srcs[ 0 ] = src;
    752 	return renderer_new_shader( config );
    753 }
    754 
    755 void renderer_delete_shader( Shader shader ) {
    756 	glDeleteProgram( shader );
    757 }
    758 
    759 static u32 mipmap_dim( u32 dim, u32 level ) {
    760 	return max( dim >> level, u32( 1 ) );
    761 }
    762 
    763 // TODO: use clz
    764 static u32 mipmap_levels( u32 width, u32 height ) {
    765 	u32 levels = 1;
    766 	while( mipmap_dim( width, levels ) > 1 || mipmap_dim( height, levels ) > 1 ) {
    767 		levels++;
    768 	}
    769 	return levels;
    770 }
    771 
    772 static u32 texture_byte_size( u32 width, u32 height, TextureFormat format, bool mipmaps ) {
    773 	u32 total_dim = width * height;
    774 	if( mipmaps ) {
    775 		for( u32 i = 1; i < mipmap_levels( width, height ); i++ ) {
    776 			total_dim += mipmap_dim( width, i ) * mipmap_dim( height, i );
    777 		}
    778 	}
    779 
    780 	switch( format ) {
    781 		case TEXFMT_INVALID:
    782 		case TEXFMT_DEPTH:
    783 			break;
    784 
    785 		case TEXFMT_R_U8:
    786 		case TEXFMT_R_U8NORM:
    787 			return total_dim * 1 * sizeof( u8 );
    788 		case TEXFMT_R_U16:
    789 			return total_dim * 1 * sizeof( u16 );
    790 		case TEXFMT_R_FLOAT:
    791 			return total_dim * 1 * sizeof( float );
    792 
    793 		case TEXFMT_RGB_U8:
    794 			return total_dim * 3 * sizeof( u8 );
    795 		case TEXFMT_RGB_U8_SRGB:
    796 			return total_dim * 3 * sizeof( u8 );
    797 		case TEXFMT_RGB_FLOAT:
    798 			return total_dim * 3 * sizeof( float );
    799 
    800 		case TEXFMT_RGBA_U8:
    801 			return total_dim * 4 * sizeof( u8 );
    802 		case TEXFMT_RGBA_U8_SRGB:
    803 			return total_dim * 4 * sizeof( u8 );
    804 		case TEXFMT_RGBA_FLOAT:
    805 			return total_dim * 4 * sizeof( float );
    806 
    807 		case TEXFMT_BC1:
    808 		case TEXFMT_BC1_SRGB:
    809 			return total_dim / 2;
    810 		case TEXFMT_BC4:
    811 			return total_dim / 2;
    812 		case TEXFMT_BC5:
    813 			return total_dim;
    814 	}
    815 
    816 	FATAL( "unsupported TextureFormat: {}", format );
    817 	return GL_INVALID_ENUM;
    818 }
    819 
    820 Texture renderer_new_texture( TextureConfig config ) {
    821 	ASSERT( config.format != TEXFMT_INVALID );
    822 	ASSERT( !config.has_mipmaps ); // TODO
    823 
    824 	GLuint texture;
    825 	glGenTextures( 1, &texture );
    826 	glBindTexture( GL_TEXTURE_2D, texture );
    827 	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texturewrap_to_glenum( config.wrap ) );
    828 	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texturewrap_to_glenum( config.wrap ) );
    829 
    830 	GLenum min_filter, mag_filter;
    831 	texturefilter_to_glenum( config.filter, config.has_mipmaps, &min_filter, &mag_filter );
    832 	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter );
    833 	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter );
    834 
    835 	if( config.wrap == TEXWRAP_BORDER ) {
    836 		glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, ( GLfloat * ) &config.border_colour );
    837 	}
    838 
    839 	GLenum internal_format = texfmt_to_internal_format( config.format );
    840 
    841 	if( is_compressed( config.format ) ) {
    842 		u32 dxt_size = texture_byte_size( config.width, config.height, config.format, false );
    843 		glCompressedTexImage2D( GL_TEXTURE_2D, 0, internal_format,
    844 			config.width, config.height, 0, dxt_size, config.data );
    845 	}
    846 	else {
    847 		GLenum channels = GL_INVALID_ENUM;
    848 		GLenum type = GL_INVALID_ENUM;
    849 
    850 		switch( config.format ) {
    851 			case TEXFMT_R_U8:
    852 			case TEXFMT_R_U8NORM:
    853 				channels = GL_RED;
    854 				type = GL_UNSIGNED_BYTE;
    855 				break;
    856 			case TEXFMT_R_U16:
    857 				channels = GL_RED;
    858 				type = GL_UNSIGNED_SHORT;
    859 				break;
    860 			case TEXFMT_R_FLOAT:
    861 				channels = GL_RED;
    862 				type = GL_FLOAT;
    863 				break;
    864 
    865 			case TEXFMT_RGB_U8:
    866 			case TEXFMT_RGB_U8_SRGB:
    867 				channels = GL_RGB;
    868 				type = GL_UNSIGNED_BYTE;
    869 				break;
    870 			case TEXFMT_RGB_FLOAT:
    871 				channels = GL_RGB;
    872 				type = GL_FLOAT;
    873 				break;
    874 
    875 			case TEXFMT_RGBA_U8:
    876 			case TEXFMT_RGBA_U8_SRGB:
    877 				channels = GL_RGBA;
    878 				type = GL_UNSIGNED_BYTE;
    879 				break;
    880 			case TEXFMT_RGBA_FLOAT:
    881 				channels = GL_RGBA;
    882 				type = GL_FLOAT;
    883 				break;
    884 
    885 			case TEXFMT_DEPTH:
    886 				channels = GL_DEPTH_COMPONENT;
    887 				type = GL_FLOAT;
    888 				break;
    889 
    890 			default:
    891 				FATAL( "implement channel/type selection for {}", config.format );
    892 				break;
    893 		}
    894 
    895 		glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
    896 			config.width, config.height, 0, channels, type, config.data );
    897 	}
    898 
    899 	return texture;
    900 }
    901 
    902 void renderer_delete_texture( Texture texture ) {
    903 	glDeleteTextures( 1, &texture );
    904 }
    905 
    906 static GLvoid * offset_to_glvoidp( u32 offset ) {
    907 	return checked_cast< GLvoid * >( checked_cast< uptr >( offset ) );
    908 }
    909 
    910 Mesh renderer_new_mesh( MeshConfig config ) {
    911 	switch( config.primitive_type ) {
    912 		case PRIMITIVETYPE_TRIANGLES:
    913 			ASSERT( config.num_vertices % 3 == 0 );
    914 			break;
    915 		case PRIMITIVETYPE_TRIANGLE_STRIP:
    916 			ASSERT( config.num_vertices >= 3 );
    917 			break;
    918 		case PRIMITIVETYPE_POINTS:
    919 			break;
    920 		case PRIMITIVETYPE_LINES:
    921 			ASSERT( config.num_vertices % 2 == 0 );
    922 			break;
    923 	}
    924 
    925 	GLuint vao;
    926 	glGenVertexArrays( 1, &vao );
    927 	glBindVertexArray( vao );
    928 
    929 	if( config.unified_buffer == 0 ) {
    930 		ASSERT( config.positions != 0 );
    931 
    932 		glBindBuffer( GL_ARRAY_BUFFER, config.positions );
    933 		glEnableVertexAttribArray( ATTR_POSITION );
    934 		glVertexAttribPointer( ATTR_POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0 );
    935 
    936 		if( config.normals != 0 ) {
    937 			glBindBuffer( GL_ARRAY_BUFFER, config.normals );
    938 			glEnableVertexAttribArray( ATTR_NORMAL );
    939 			glVertexAttribPointer( ATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, 0, 0 );
    940 		}
    941 
    942 		if( config.tex_coords0 != 0 ) {
    943 			glBindBuffer( GL_ARRAY_BUFFER, config.tex_coords0 );
    944 			glEnableVertexAttribArray( ATTR_TEX_COORD0 );
    945 			glVertexAttribPointer( ATTR_TEX_COORD0, 2, GL_FLOAT, GL_FALSE, 0, 0 );
    946 		}
    947 
    948 		if( config.tex_coords1 != 0 ) {
    949 			glBindBuffer( GL_ARRAY_BUFFER, config.tex_coords1 );
    950 			glEnableVertexAttribArray( ATTR_TEX_COORD1 );
    951 			glVertexAttribPointer( ATTR_TEX_COORD1, 2, GL_FLOAT, GL_FALSE, 0, 0 );
    952 		}
    953 
    954 		if( config.colours != 0 ) {
    955 			glBindBuffer( GL_ARRAY_BUFFER, config.colours );
    956 			glEnableVertexAttribArray( ATTR_COLOUR );
    957 			glVertexAttribPointer( ATTR_COLOUR, 3, GL_FLOAT, GL_FALSE, 0, 0 );
    958 		}
    959 	}
    960 	else {
    961 		glBindBuffer( GL_ARRAY_BUFFER, config.unified_buffer );
    962 
    963 		glEnableVertexAttribArray( ATTR_POSITION );
    964 		glVertexAttribPointer( ATTR_POSITION, 3, GL_FLOAT, GL_FALSE, config.stride, offset_to_glvoidp( config.positions_offset ) );
    965 
    966 		if( config.normals_offset != 0 ) {
    967 			glEnableVertexAttribArray( ATTR_NORMAL );
    968 			glVertexAttribPointer( ATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, config.stride, offset_to_glvoidp( config.normals_offset ) );
    969 		}
    970 
    971 		if( config.tex_coords0_offset != 0 ) {
    972 			glEnableVertexAttribArray( ATTR_TEX_COORD0 );
    973 			glVertexAttribPointer( ATTR_TEX_COORD0, 2, GL_FLOAT, GL_FALSE, config.stride, offset_to_glvoidp( config.tex_coords0_offset ) );
    974 		}
    975 
    976 		if( config.tex_coords1_offset != 0 ) {
    977 			glEnableVertexAttribArray( ATTR_TEX_COORD1 );
    978 			glVertexAttribPointer( ATTR_TEX_COORD1, 2, GL_FLOAT, GL_FALSE, config.stride, offset_to_glvoidp( config.tex_coords1_offset ) );
    979 		}
    980 
    981 		if( config.colours_offset != 0 ) {
    982 			glEnableVertexAttribArray( ATTR_COLOUR );
    983 			glVertexAttribPointer( ATTR_COLOUR, 3, GL_FLOAT, GL_FALSE, config.stride, offset_to_glvoidp( config.colours_offset ) );
    984 		}
    985 	}
    986 
    987 	if( config.indices != 0 ) {
    988 		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, config.indices );
    989 	}
    990 
    991 	glBindVertexArray( 0 );
    992 
    993 	Mesh mesh = { };
    994 	mesh.num_vertices = config.num_vertices;
    995 	mesh.primitive_type = config.primitive_type;
    996 	mesh.vao = vao;
    997 	if( config.unified_buffer == 0 ) {
    998 		mesh.positions = config.positions;
    999 		mesh.normals = config.normals;
   1000 		mesh.tex_coords0 = config.tex_coords0;
   1001 		mesh.tex_coords1 = config.tex_coords1;
   1002 		mesh.colours = config.colours;
   1003 	}
   1004 	else {
   1005 		mesh.positions = config.unified_buffer;
   1006 	}
   1007 	mesh.indices = config.indices;
   1008 
   1009 	return mesh;
   1010 }
   1011 
   1012 void renderer_delete_mesh( const Mesh & mesh ) {
   1013 	DeleteCommand command;
   1014 	command.type = DELETE_MESH;
   1015 	command.mesh = mesh;
   1016 	deletes.append( command );
   1017 }
   1018 
   1019 void renderer_draw_mesh( const Mesh & mesh, const RenderState & render_state ) {
   1020 	ASSERT( in_frame && in_pass );
   1021 
   1022 	DrawCall dc;
   1023 	dc.mesh = mesh;
   1024 	dc.render_state = render_state;
   1025 	dc.num_instances = 0;
   1026 	dc.instance_data = 0;
   1027 	draw_calls.append( dc );
   1028 
   1029 	draw_calls_this_frame++;
   1030 	vertices_this_frame += mesh.num_vertices;
   1031 }
   1032 
   1033 void renderer_draw_instances( const Mesh & mesh, const RenderState & render_state, u32 num_instances, VB instance_data ) {
   1034 	ASSERT( in_frame && in_pass );
   1035 
   1036 	if( num_instances == 0 ) {
   1037 		return;
   1038 	}
   1039 
   1040 	DrawCall dc;
   1041 	dc.mesh = mesh;
   1042 	dc.render_state = render_state;
   1043 	dc.num_instances = num_instances;
   1044 	dc.instance_data = instance_data;
   1045 	draw_calls.append( dc );
   1046 
   1047 	draw_calls_this_frame++;
   1048 	vertices_this_frame += mesh.num_vertices * num_instances;
   1049 }