medfall

A super great game engine
Log | Files | Refs

renderer.cc (30785B)


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