commit 795f836126f2fed1cec1e4844aa7472c6e4ec758
parent 9569cab05acbab7265789a651a6db859f9e6be19
Author: Michael Savage <mikejsavage@gmail.com>
Date: Fri, 2 Aug 2019 17:01:45 +0300
Set uniforms/textures by name
Diffstat:
17 files changed, 217 insertions(+), 275 deletions(-)
diff --git a/bsp.cc b/bsp.cc
@@ -353,7 +353,7 @@ static v2 screen_pos( const m4 & P, const m4 & V, v3 camera_pos, v3 target, v2 c
}
GAME_INIT( game_init ) {
- bsp_init( &game->bsp, "assets/cocaine_b2.bsp" );
+ bsp_init( &game->bsp, "assets/acidwdm2.bsp" );
game->pos = v3( 0, -100, 450 );
game->pitch = 0;
@@ -448,8 +448,8 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS_TO_GBUFFER );
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
- render_state.uniforms[ UNIFORMS_MODEL ] = world_model_uniforms;
+ render_state.set_uniform( "view", view_uniforms );
+ render_state.set_uniform( "model", world_model_uniforms );
render_state.cull_face = CULLFACE_FRONT;
bspr_render( &game->bspr, render_state );
@@ -475,7 +475,7 @@ GAME_FRAME( game_frame ) {
//
// immediate_render( render_state );
//
- render_state.uniforms[ UNIFORMS_MODEL ] = tree_model_uniforms;
+ render_state.set_uniform( "model", tree_model_uniforms );
renderer_draw_mesh( tree_mesh, render_state );
renderer_end_pass();
@@ -492,8 +492,8 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS_TO_GBUFFER );
render_state.depth_func = DEPTHFUNC_ALWAYS;
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
- render_state.uniforms[ UNIFORMS_MODEL ] = tree_model_uniforms;
+ render_state.set_uniform( "view", view_uniforms );
+ render_state.set_uniform( "model", tree_model_uniforms );
renderer_draw_mesh( tree_mesh, render_state );
@@ -507,7 +507,7 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_DEPTH_EDGE );
render_state.depth_func = DEPTHFUNC_ALWAYS;
- render_state.textures[ 0 ] = model_depth.textures[ OUTPUT_DEPTH ];
+ render_state.set_texture( "model_depth", model_depth.textures[ OUTPUT_DEPTH ] );
renderer_draw_fullscreen_triangle( render_state );
@@ -520,14 +520,14 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_GBUFFER );
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
- render_state.uniforms[ UNIFORMS_MODEL ] = world_model_uniforms;
+ render_state.set_uniform( "view", view_uniforms );
+ render_state.set_uniform( "model", world_model_uniforms );
render_state.cull_face = CULLFACE_FRONT;
// render_state.depth_func = DEPTHFUNC_EQUAL;
- render_state.textures[ 0 ] = world_depth_and_normals.textures[ OUTPUT_NORMAL ];
- render_state.textures[ 1 ] = world_depth_and_normals.textures[ OUTPUT_DEPTH ];
- render_state.textures[ 2 ] = model_depth_outline.textures[ OUTPUT_DEPTH ];
- render_state.textures[ 3 ] = renderer_blue_noise();
+ render_state.set_texture( "world_normal", world_depth_and_normals.textures[ OUTPUT_NORMAL ] );
+ render_state.set_texture( "world_depth", world_depth_and_normals.textures[ OUTPUT_DEPTH ] );
+ render_state.set_texture( "outline_depth", model_depth_outline.textures[ OUTPUT_DEPTH ] );
+ render_state.set_texture( "blue_noise", renderer_blue_noise() );
bspr_render( &game->bspr, render_state );
@@ -552,7 +552,7 @@ GAME_FRAME( game_frame ) {
//
// immediate_render( render_state );
- render_state.uniforms[ UNIFORMS_MODEL ] = tree_model_uniforms;
+ render_state.set_uniform( "model", tree_model_uniforms );
renderer_draw_mesh( tree_mesh, render_state );
renderer_end_pass();
diff --git a/clipmap.cc b/clipmap.cc
@@ -775,19 +775,19 @@ GAME_FRAME( game_frame ) {
{
RenderState render_state;
render_state.shader = get_shader( SHADER_CLIPMAP );
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
- render_state.uniforms[ UNIFORMS_SUN ] = sun_uniforms;
- render_state.textures[ 0 ] = clipmap.gpu.heightmap;
- render_state.textures[ 1 ] = clipmap.gpu.normalmap;
- render_state.textures[ 2 ] = clipmap.gpu.horizonmap;
- render_state.textures[ 3 ] = renderer_blue_noise();
+ render_state.set_uniform( "view", view_uniforms );
+ render_state.set_uniform( "sun", sun_uniforms );
+ render_state.set_texture( "heightmap", clipmap.gpu.heightmap );
+ render_state.set_texture( "normalmap", clipmap.gpu.normalmap );
+ render_state.set_texture( "horizonmap", clipmap.gpu.horizonmap );
+ render_state.set_texture( "blue_noise", renderer_blue_noise() );
render_state.wireframe = game->draw_wireframe;
// draw cross
{
v2 snapped_pos = floorf( game->frozen_pos.xy() );
- render_state.uniforms[ UNIFORMS_MODEL ] = rotation_uniforms[ 0 ];
- render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( snapped_pos, 1.0f );
+ render_state.set_uniform( "model", rotation_uniforms[ 0 ] );
+ render_state.set_uniform( "clipmap", renderer_uniforms( snapped_pos, 1.0f ) );
renderer_draw_mesh( clipmap.gpu.cross, render_state );
}
@@ -818,16 +818,16 @@ GAME_FRAME( game_frame ) {
inside = false;
Mesh mesh = inside ? clipmap.gpu.tile : clipmap.gpu.empty_tile;
- render_state.uniforms[ UNIFORMS_MODEL ] = rotation_uniforms[ 0 ];
- render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( tile_tl, scale );
+ render_state.set_uniform( "model", rotation_uniforms[ 0 ] );
+ render_state.set_uniform( "clipmap", renderer_uniforms( tile_tl, scale ) );
renderer_draw_mesh( mesh, render_state );
}
}
// draw filler
{
- render_state.uniforms[ UNIFORMS_MODEL ] = rotation_uniforms[ 0 ];
- render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( snapped_pos, scale );
+ render_state.set_uniform( "model", rotation_uniforms[ 0 ] );
+ render_state.set_uniform( "clipmap", renderer_uniforms( snapped_pos, scale ) );
renderer_draw_mesh( clipmap.gpu.filler, render_state );
}
@@ -844,8 +844,8 @@ GAME_FRAME( game_frame ) {
r |= d.x >= scale ? 0 : 2;
r |= d.y >= scale ? 0 : 1;
- render_state.uniforms[ UNIFORMS_MODEL ] = rotation_uniforms[ r ];
- render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( tile_centre, scale );
+ render_state.set_uniform( "model", rotation_uniforms[ r ] );
+ render_state.set_uniform( "clipmap", renderer_uniforms( tile_centre, scale ) );
renderer_draw_mesh( clipmap.gpu.trim, render_state );
}
@@ -853,8 +853,8 @@ GAME_FRAME( game_frame ) {
{
v2 next_base = next_snapped_pos - v2( checked_cast< float >( TILE_RESOLUTION << ( l + 1 ) ) );
- render_state.uniforms[ UNIFORMS_MODEL ] = rotation_uniforms[ 0 ];
- render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( next_base, scale );
+ render_state.set_uniform( "model", rotation_uniforms[ 0 ] );
+ render_state.set_uniform( "clipmap", renderer_uniforms( next_base, scale ) );
renderer_draw_mesh( clipmap.gpu.seam, render_state );
}
}
@@ -867,10 +867,10 @@ GAME_FRAME( game_frame ) {
RenderState skirt_render_state;
skirt_render_state.shader = get_shader( SHADER_CLIPMAP_SKIRT );
- skirt_render_state.uniforms[ UNIFORMS_VIEW ] = inf_view_uniforms;
- skirt_render_state.uniforms[ UNIFORMS_SUN ] = sun_uniforms;
- skirt_render_state.uniforms[ UNIFORMS_CLIPMAP_SKIRT ] = renderer_uniforms( scale );
- skirt_render_state.textures[ 0 ] = renderer_blue_noise();
+ skirt_render_state.set_uniform( "view", inf_view_uniforms );
+ skirt_render_state.set_uniform( "sun", sun_uniforms );
+ skirt_render_state.set_uniform( "clipmap_skirt", renderer_uniforms( scale ) );
+ skirt_render_state.set_texture( "blue_noise", renderer_blue_noise() );
skirt_render_state.wireframe = game->draw_wireframe;
renderer_draw_mesh( clipmap.gpu.skirt, skirt_render_state );
}
@@ -879,9 +879,9 @@ GAME_FRAME( game_frame ) {
{
RenderState render_state;
render_state.shader = get_shader( SHADER_TREE );
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
- render_state.uniforms[ UNIFORMS_SUN ] = renderer_uniforms( sun_dir );
- render_state.textures[ 0 ] = renderer_blue_noise();
+ render_state.set_uniform( "view", view_uniforms );
+ render_state.set_uniform( "sun", renderer_uniforms( sun_dir ) );
+ render_state.set_texture( "blue_noise", renderer_blue_noise() );
renderer_draw_instances( tree_mesh, render_state, num_trees, trees_instance_data );
}
@@ -961,7 +961,7 @@ GAME_FRAME( game_frame ) {
RenderState fireball_render_state;
fireball_render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
- fireball_render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
+ fireball_render_state.set_uniform( "view", view_uniforms );
immediate_render( fireball_render_state );
for( size_t i = 0; i < explosions.elems.n; i++ ) {
@@ -978,7 +978,7 @@ GAME_FRAME( game_frame ) {
RenderState explosion_render_state;
explosion_render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
- explosion_render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
+ explosion_render_state.set_uniform( "view", view_uniforms );
immediate_render( explosion_render_state );
}
@@ -1082,7 +1082,7 @@ GAME_FRAME( game_frame ) {
RenderState impact_render_state;
impact_render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
- impact_render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
+ impact_render_state.set_uniform( "view", view_uniforms );
immediate_render( impact_render_state );
}
@@ -1093,7 +1093,7 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_WIREFRAME );
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
+ render_state.set_uniform( "view", view_uniforms );
render_state.wireframe = true;
immediate_render( render_state );
}
diff --git a/fnv.h b/fnv.h
@@ -3,6 +3,16 @@
#include "intrinsics.h"
#include "array.h"
+u32 fnv1a32( const void * data, size_t n, u32 hash = UINT32_C( 2166136261 ) ) {
+ const u32 prime = U32( 16777619 );
+
+ const char * cdata = ( const char * ) data;
+ for( size_t i = 0; i < n; i++ ) {
+ hash = ( hash ^ cdata[ i ] ) * prime;
+ }
+ return hash;
+}
+
forceinline u64 fnv1a( const void * data, size_t n ) {
const u64 basis = U64( 14695981039346656037 );
const u64 prime = U64( 1099511628211 );
diff --git a/fullscreen.cc b/fullscreen.cc
@@ -21,7 +21,7 @@ static v3 light_dir = v3_forward( -60, 90 );
static void draw_scene( RenderState render_state, bool draw_light = false ) {
m4 I = m4_identity();
- render_state.uniforms[ UNIFORMS_MODEL ] = renderer_uniforms( I );
+ render_state.set_uniform( "model", renderer_uniforms( I ) );
immediate_sphere( v3( 0, 0, 2 ), 2, v4( 1, 0, 0, 1 ) );
immediate_sphere( v3( -3, 7, 2 ), 2, v4( 0, 1, 0, 1 ) );
@@ -46,7 +46,7 @@ static void draw_scene( RenderState render_state, bool draw_light = false ) {
immediate_render( render_state );
m4 M = m4_translation( v3( -7, -2, 0 ) ) * m4_rotx( deg_to_rad( 90 ) );
- render_state.uniforms[ UNIFORMS_MODEL ] = renderer_uniforms( M );
+ render_state.set_uniform( "model", renderer_uniforms( M ) );
renderer_draw_mesh( tree_mesh, render_state );
}
@@ -157,7 +157,7 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_WRITE_SHADOW_MAP );
- render_state.uniforms[ UNIFORMS_LIGHT_VIEW ] = light_view_uniforms;
+ render_state.set_uniform( "light_view", light_view_uniforms );
draw_scene( render_state );
@@ -170,10 +170,10 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_SHADOWED_VERTEX_COLOURS_TO_GBUFFER );
- render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( V, P, game->pos );
- render_state.uniforms[ UNIFORMS_LIGHT_VIEW ] = light_view_uniforms;
- render_state.textures[ 0 ] = shadow_fb.textures[ OUTPUT_DEPTH ];
- render_state.textures[ 1 ] = renderer_blue_noise();
+ render_state.set_uniform( "view", renderer_uniforms( V, P, game->pos ) );
+ render_state.set_uniform( "light_view", light_view_uniforms );
+ render_state.set_texture( "shadowmap", shadow_fb.textures[ OUTPUT_DEPTH ] );
+ render_state.set_texture( "dither_noise", renderer_blue_noise() );
draw_scene( render_state, true );
@@ -187,10 +187,10 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_GBUFFER );
render_state.depth_func = DEPTHFUNC_DISABLED;
- render_state.textures[ 0 ] = gbuffer.textures[ OUTPUT_ALBEDO ];
- render_state.textures[ 1 ] = gbuffer.textures[ OUTPUT_NORMAL ];
- render_state.textures[ 2 ] = gbuffer.textures[ OUTPUT_DEPTH ];
- render_state.textures[ 3 ] = renderer_blue_noise();
+ render_state.set_texture( "outline_depth", gbuffer.textures[ OUTPUT_ALBEDO ] );
+ render_state.set_texture( "world_normal", gbuffer.textures[ OUTPUT_NORMAL ] );
+ render_state.set_texture( "world_depth", gbuffer.textures[ OUTPUT_DEPTH ] );
+ render_state.set_texture( "blue_noise", renderer_blue_noise() );
renderer_draw_fullscreen_triangle( render_state );
diff --git a/gltf.cc b/gltf.cc
@@ -553,10 +553,9 @@ GAME_FRAME( game_frame ) {
{
RenderState render_state;
render_state.shader = get_shader( SHADER_SKINNED_FLAT_VERTEX_COLOURS );
- render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms;
- render_state.uniforms[ UNIFORMS_MODEL ] = model_uniforms;
- render_state.uniforms[ UNIFORMS_JOINT_POSES ] = joints_uniforms;
- render_state.textures[ 0 ] = renderer_blue_noise();
+ render_state.set_uniform( "view", view_uniforms );
+ render_state.set_uniform( "model", model_uniforms );
+ render_state.set_uniform( "animation", joints_uniforms );
render_state.wireframe = input->keys[ KEY_M ];
for( u32 i = 0; i < model.num_meshes; i++ ) {
@@ -574,7 +573,7 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
- render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( V, P );
+ render_state.set_uniform( "view", renderer_uniforms( V, P ) );
immediate_render( render_state );
}
}
@@ -593,7 +592,7 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
- render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( V, P );
+ render_state.set_uniform( "view", renderer_uniforms( V, P ) );
immediate_render( render_state );
}
diff --git a/immediate.cc b/immediate.cc
@@ -190,13 +190,6 @@ void immediate_arrow( v3 origin, v3 direction, float length, v4 colour ) {
}
}
-void immediate_render( Shader shader, Texture texture ) {
- RenderState render_state;
- render_state.shader = shader;
- render_state.textures[ 0 ] = texture;
- immediate_render( render_state );
-}
-
void immediate_render( const RenderState & render_state ) {
if( triangles.size() == 0 ) {
return;
@@ -211,7 +204,7 @@ void immediate_render( const RenderState & render_state ) {
mesh_config.normals_offset = checked_cast< u32 >( offsetof( ImmediateVertex, normal ) );
mesh_config.colours_offset = checked_cast< u32 >( offsetof( ImmediateVertex, colour ) );
mesh_config.num_vertices = triangles.size() * 3;
- if( render_state.textures[ 0 ] != 0 ) {
+ if( render_state.num_textures > 0 ) {
mesh_config.tex_coords0_offset = checked_cast< u32 >( offsetof( ImmediateVertex, uv ) );
}
diff --git a/immediate.h b/immediate.h
@@ -17,5 +17,4 @@ void immediate_sphere( v3 centre, float radius, v4 colour, u32 subdivisions = 16
void immediate_aabb( v3 mins, v3 maxs, v4 colour );
void immediate_arrow( v3 origin, v3 direction, float length, v4 colour );
-void immediate_render( Shader shader, Texture texture = 0 );
void immediate_render( const RenderState & render_state );
diff --git a/make.lua b/make.lua
@@ -50,8 +50,8 @@ end
require( "libs/squish" )
--- bin( "bsp", { "main", "bsp", "bsp_renderer", game_objs }, { game_libs } )
--- bin( "sm", { "main", "shadow_map", game_objs }, { game_libs } )
+bin( "bsp", { "main", "bsp", "bsp_renderer", game_objs }, { game_libs } )
+bin( "sm", { "main", "shadow_map", game_objs }, { game_libs } )
bin( "gltf", { "main", "gltf", game_objs }, { "cgltf", game_libs } )
bin( "msdf", { "main", "msdf", game_objs }, game_libs )
bin( "fullscreen", { "main", "fullscreen", game_objs }, { game_libs } )
diff --git a/msdf.cc b/msdf.cc
@@ -91,7 +91,7 @@ static void msdf_text( const char * str, int x, int y, float scale ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_MSDF );
- render_state.textures[ 0 ] = atlas;
+ render_state.set_texture( "atlas", atlas );
render_state.depth_func = DEPTHFUNC_DISABLED;
render_state.enable_alpha_blending = true;
render_state.disable_depth_writes = true;
diff --git a/renderer.cc b/renderer.cc
@@ -8,12 +8,11 @@
#include "linear_algebra.h"
#include "blue_noise.h"
#include "obj.h"
+#include "fnv.h"
STATIC_ASSERT( SAME_TYPE( VB, GLuint ) );
STATIC_ASSERT( SAME_TYPE( IB, GLuint ) );
-STATIC_ASSERT( SAME_TYPE( Shader, GLuint ) );
STATIC_ASSERT( SAME_TYPE( Texture, GLuint ) );
-STATIC_ASSERT( SAME_TYPE( TextureBufferObject, GLuint ) );
STATIC_ASSERT( SAME_TYPE( FramebufferObject, GLuint ) );
STATIC_ASSERT( SAME_TYPE( u32, GLuint ) );
@@ -38,7 +37,6 @@ struct DrawCall {
enum DeleteCommandType {
DELETE_VB,
DELETE_IB,
- DELETE_TB,
DELETE_TEXTURE,
DELETE_MESH,
};
@@ -48,7 +46,6 @@ struct DeleteCommand {
union {
VB vb;
IB ib;
- TB tb;
Shader shader;
Texture texture;
Mesh mesh;
@@ -161,41 +158,45 @@ static GLenum primitivetype_to_glenum( PrimitiveType primitive_type ) {
return GL_INVALID_ENUM;
}
-static bool operator!=( UniformBinding a, UniformBinding b ) {
- return a.offset != b.offset || a.size != b.size;
-}
-
static void set_render_state( const RenderState & state ) {
if( state.shader != previous_render_state.shader ) {
- glUseProgram( state.shader );
+ glUseProgram( state.shader->program );
}
// uniforms
- for( GLuint i = 0; i < UNIFORMS_COUNT; i++ ) {
- if( state.uniforms[ i ] != previous_render_state.uniforms[ i ] ) {
- UniformBinding binding = state.uniforms[ i ];
- if( binding.size == 0 ) {
- glBindBufferBase( GL_UNIFORM_BUFFER, i, 0 );
- }
- else {
+ // TODO: maybe these shouldn't always get rebound
+ for( size_t i = 0; i < ARRAY_COUNT( state.shader->uniforms ); i++ ) {
+ bool found = false;
+ for( size_t j = 0; j < state.num_uniforms; j++ ) {
+ UniformBinding binding = state.uniforms[ j ].binding;
+ if( state.uniforms[ j ].name_hash == state.shader->uniforms[ i ] && binding.size > 0 ) {
glBindBufferRange( GL_UNIFORM_BUFFER, i, uniforms, binding.offset, binding.size );
+ found = true;
+ break;
}
}
+
+ if( !found ) {
+ glBindBufferBase( GL_UNIFORM_BUFFER, i, 0 );
+ }
}
// textures
- for( GLuint i = 0; i < RENDERER_MAX_TEXTURES; i++ ) {
- if( state.textures[ i ] != previous_render_state.textures[ i ] ) {
- glActiveTexture( GL_TEXTURE0 + i );
- glBindTexture( GL_TEXTURE_2D, state.textures[ i ] );
+ for( size_t i = 0; i < ARRAY_COUNT( state.shader->textures ); i++ ) {
+ glActiveTexture( GL_TEXTURE0 + i );
+ // ggprint( "shader texture = {}\n", state.shader->textures[ i ] );
+
+ bool found = false;
+ for( size_t j = 0; j < state.num_textures; j++ ) {
+ if( state.textures[ j ].name_hash == state.shader->textures[ i ] ) {
+ glBindTexture( GL_TEXTURE_2D, state.textures[ j ].texture );
+ found = true;
+ break;
+ }
}
- }
- // texture buffers
- for( GLuint i = 0; i < RENDERER_MAX_TEXTURE_BUFFERS; i++ ) {
- if( state.tbs[ i ].texture != previous_render_state.tbs[ i ].texture ) {
- glActiveTexture( GL_TEXTURE0 + i + RENDERER_MAX_TEXTURES );
- glBindTexture( GL_TEXTURE_BUFFER, state.tbs[ i ].texture );
+ if( !found ) {
+ glBindTexture( GL_TEXTURE_2D, 0 );
}
}
@@ -364,11 +365,6 @@ void renderer_end_frame() {
glDeleteBuffers( 1, &del.ib );
break;
- case DELETE_TB:
- glDeleteBuffers( 1, &del.tb.tbo );
- glDeleteTextures( 1, &del.tb.texture );
- break;
-
case DELETE_TEXTURE:
glDeleteTextures( 1, &del.texture );
break;
@@ -444,6 +440,7 @@ UniformBinding renderer_upload_uniforms( const void * data, size_t size, size_t
alignment = max( alignment, ubo_offset_alignment );
UniformBinding binding;
+ binding.ubo = uniforms;
binding.offset = checked_cast< u32 >( align_power_of_2( uniforms_offset, alignment ) );
binding.size = checked_cast< u32 >( size );
@@ -590,41 +587,6 @@ void renderer_delete_ib( IB ib ) {
glDeleteBuffers( 1, &ib );
}
-TB renderer_new_tb( TextureFormat format, const void * data, u32 len, BufferUsage usage ) {
- ASSERT( len < S32_MAX );
- ASSERT( !is_compressed( format ) );
-
- GLuint tbo, texture;
- glGenBuffers( 1, &tbo );
- glGenTextures( 1, &texture );
-
- glBindBuffer( GL_TEXTURE_BUFFER, tbo );
- glBindTexture( GL_TEXTURE_BUFFER, texture );
- glTexBuffer( GL_TEXTURE_BUFFER, texfmt_to_internal_format( format ), tbo );
-
- if( data != NULL ) {
- glBufferData( GL_TEXTURE_BUFFER, len, data, bufferusage_to_glenum( usage ) );
- }
-
- TB tb = { };
- tb.tbo = tbo;
- tb.texture = texture;
- return tb;
-}
-
-void renderer_tb_data( TB tb, const void * data, u32 len, BufferUsage usage ) {
- ASSERT( len < S32_MAX );
-
- glBindBuffer( GL_TEXTURE_BUFFER, tb.tbo );
- glBufferData( GL_TEXTURE_BUFFER, len, NULL, bufferusage_to_glenum( usage ) );
- glBufferData( GL_TEXTURE_BUFFER, len, data, bufferusage_to_glenum( usage ) );
-}
-
-void renderer_delete_tb( TB tb ) {
- glDeleteBuffers( 1, &tb.tbo );
- glDeleteTextures( 1, &tb.texture );
-}
-
static GLenum framebufferattachment_to_glenum( FramebufferAttachment attachment ) {
switch( attachment ) {
case FB_COLOUR: return GL_COLOR_ATTACHMENT0;
@@ -737,25 +699,26 @@ static GLuint new_gl_shader( GLenum type, array< const char * > srcs ) {
char buf[ 1024 ];
glGetShaderInfoLog( shader, sizeof( buf ), NULL, buf );
WARN( "shader compilation failed: {}", buf );
+ glDeleteShader( shader );
// static char src[ 65536 ];
// glGetShaderSource( shader, sizeof( src ), NULL, src );
// printf( "%s\n", src );
- return INVALID_SHADER;
+ return 0;
}
return shader;
}
-Shader renderer_new_shader( ShaderConfig config ) {
- GLuint vs = new_gl_shader( GL_VERTEX_SHADER, config.srcs );
- GLuint fs = new_gl_shader( GL_FRAGMENT_SHADER, config.srcs );
+Shader renderer_new_shader( array< const char * > srcs ) {
+ Shader shader = { };
+
+ GLuint vs = new_gl_shader( GL_VERTEX_SHADER, srcs );
+ GLuint fs = new_gl_shader( GL_FRAGMENT_SHADER, srcs );
if( vs == 0 || fs == 0 ) {
- if( vs != 0 ) glDeleteShader( vs );
- if( fs != 0 ) glDeleteShader( fs );
- return INVALID_SHADER;
+ return shader;
}
GLuint program = glCreateProgram();
@@ -786,55 +749,51 @@ Shader renderer_new_shader( ShaderConfig config ) {
glGetProgramInfoLog( program, sizeof( buf ), NULL, buf );
WARN( "shader linking failed: {}", buf );
- return INVALID_SHADER;
+ return shader;
}
- const char * ubo_names[] = { "debug", "view", "model", "light_view", "window", "sun", "sky", "clipmap", "clipmap_skirt", "animation" };
- STATIC_ASSERT( ARRAY_COUNT( ubo_names ) == UNIFORMS_COUNT );
- for( GLuint i = 0; i < ARRAY_COUNT( ubo_names ); i++ ) {
- GLuint idx = glGetUniformBlockIndex( program, ubo_names[ i ] );
- if( idx != GL_INVALID_INDEX ) {
- glUniformBlockBinding( program, idx, i );
- }
- }
glUseProgram( program );
- for( GLint i = 0; i < RENDERER_MAX_TEXTURES; i++ ) {
- if( config.texture_uniform_names[ i ] != NULL ) {
- GLuint uniform = glGetUniformLocation( program, config.texture_uniform_names[ i ] );
- if( uniform == GLuint( -1 ) ) {
- WARN( "couldn't find texture uniform: {}", config.texture_uniform_names[ i ] );
- glDeleteProgram( program );
- return INVALID_SHADER;
- }
- glUniform1i( uniform, i );
+ shader.program = program;
+
+ GLint count, maxlen;
+ glGetProgramiv( program, GL_ACTIVE_UNIFORMS, &count );
+ glGetProgramiv( program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxlen );
+
+ size_t numtextures = 0;
+ for( GLint i = 0; i < count; i++ ) {
+ char name[ 128 ];
+ GLint size, len;
+ GLenum type;
+ glGetActiveUniform( program, i, sizeof( name ), &len, &size, &type, name );
+
+ if( type == GL_SAMPLER_2D ) {
+ glUniform1i( i, numtextures );
+ shader.textures[ numtextures ] = fnv1a32( name, len );
+ numtextures++;
}
}
- for( GLint i = 0; i < RENDERER_MAX_TEXTURE_BUFFERS; i++ ) {
- if( config.texture_buffer_uniform_names[ i ] != NULL ) {
- GLuint uniform = glGetUniformLocation( program, config.texture_buffer_uniform_names[ i ] );
- if( uniform == GLuint( -1 ) ) {
- WARN( "couldn't find texturebuffer uniform: {}", config.texture_buffer_uniform_names[ i ] );
- glDeleteProgram( program );
- return INVALID_SHADER;
- }
- glUniform1i( uniform, i + RENDERER_MAX_TEXTURES );
- }
+ glGetProgramiv( program, GL_ACTIVE_UNIFORM_BLOCKS, &count );
+ glGetProgramiv( program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxlen );
+
+ size_t numubos = 0;
+ for( GLint i = 0; i < count; i++ ) {
+ char name[ 128 ];
+ GLint len;
+ glGetActiveUniformBlockName( program, i, sizeof( name ), &len, name );
+ glUniformBlockBinding( program, i, numubos );
+ shader.uniforms[ numubos ] = fnv1a32( name, len );
+ numubos++;
}
- glUseProgram( 0 );
- return program;
-}
+ glUseProgram( 0 );
-Shader renderer_new_shader( const char * src ) {
- ShaderConfig config;
- config.srcs[ 0 ] = src;
- return renderer_new_shader( config );
+ return shader;
}
void renderer_delete_shader( Shader shader ) {
- glDeleteProgram( shader );
+ glDeleteProgram( shader.program );
}
static u32 mipmap_dim( u32 dim, u32 level ) {
diff --git a/renderer.h b/renderer.h
@@ -3,6 +3,7 @@
#include "intrinsics.h"
#include "array.h"
#include "linear_algebra.h"
+#include "stringhash.h"
/*
* forward declare things so only the implementation includes the full GL headers
@@ -10,26 +11,9 @@
typedef u32 VB;
typedef u32 IB;
-typedef u32 Shader;
typedef u32 Texture;
-typedef u32 TextureBufferObject;
typedef u32 FramebufferObject;
-enum UniformSlots {
- UNIFORMS_DEBUG,
- UNIFORMS_VIEW,
- UNIFORMS_MODEL,
- UNIFORMS_LIGHT_VIEW,
- UNIFORMS_WINDOW,
- UNIFORMS_SUN,
- UNIFORMS_SKY,
- UNIFORMS_CLIPMAP,
- UNIFORMS_CLIPMAP_SKIRT,
- UNIFORMS_JOINT_POSES,
-
- UNIFORMS_COUNT,
-};
-
enum OutputSlots {
OUTPUT_ALBEDO,
OUTPUT_NORMAL,
@@ -42,8 +26,6 @@ enum OutputSlots {
#define RENDERER_MAX_TEXTURES 4
#define RENDERER_MAX_TEXTURE_BUFFERS 4
-const u32 INVALID_SHADER = 0;
-
enum CullFace {
CULLFACE_BACK,
CULLFACE_FRONT,
@@ -113,9 +95,10 @@ enum FramebufferAttachment {
FB_NORMAL,
};
-struct TB {
- TextureBufferObject tbo;
- Texture texture;
+struct Shader {
+ u32 program;
+ u32 uniforms[ 16 ];
+ u32 textures[ RENDERER_MAX_TEXTURES ];
};
struct FB {
@@ -125,22 +108,60 @@ struct FB {
};
struct UniformBinding {
- // TODO: might be able to make these u16
- u32 offset;
- u32 size;
+ u32 ubo;
+ u16 offset;
+ u16 size;
};
struct RenderState {
- StaticArray< UniformBinding, UNIFORMS_COUNT > uniforms = { };
- StaticArray< Texture, RENDERER_MAX_TEXTURES > textures = { };
- StaticArray< TB, RENDERER_MAX_TEXTURE_BUFFERS > tbs = { };
- Shader shader = INVALID_SHADER;
+ struct Uniform {
+ u32 name_hash;
+ UniformBinding binding;
+ };
+
+ struct T {
+ u32 name_hash;
+ Texture texture;
+ };
+
+ Uniform uniforms[ 16 ];
+ T textures[ RENDERER_MAX_TEXTURES ];
+ size_t num_uniforms = 0;
+ size_t num_textures = 0;
+
+ const Shader * shader = NULL;
DepthFunc depth_func = DEPTHFUNC_LESS;
CullFace cull_face = CULLFACE_BACK;
bool disable_depth_writes = false;
bool disable_colour_writes = false;
bool enable_alpha_blending = false;
bool wireframe = false;
+
+ void set_uniform( StringHash name, UniformBinding binding ) {
+ for( size_t i = 0; i < num_uniforms; i++ ) {
+ if( uniforms[ i ].name_hash == name.hash ) {
+ uniforms[ i ].binding = binding;
+ return;
+ }
+ }
+
+ uniforms[ num_uniforms ].name_hash = name.hash;
+ uniforms[ num_uniforms ].binding = binding;
+ num_uniforms++;
+ }
+
+ void set_texture( StringHash name, Texture texture ) {
+ for( size_t i = 0; i < num_textures; i++ ) {
+ if( textures[ i ].name_hash == name.hash ) {
+ textures[ i ].texture = texture;
+ return;
+ }
+ }
+
+ textures[ num_textures ].name_hash = name.hash;
+ textures[ num_textures ].texture = texture;
+ num_textures++;
+ }
};
enum VertexFormat {
@@ -228,12 +249,6 @@ struct MeshConfig {
PrimitiveType primitive_type = PRIMITIVETYPE_TRIANGLES;
};
-struct ShaderConfig {
- StaticArray< const char *, 8 > srcs = { };
- StaticArray< const char *, RENDERER_MAX_TEXTURES > texture_uniform_names = { };
- StaticArray< const char *, RENDERER_MAX_TEXTURE_BUFFERS > texture_buffer_uniform_names = { };
-};
-
struct TextureConfig {
u32 width = 0;
u32 height = 0;
@@ -294,16 +309,11 @@ void renderer_delete_vb( VB vb );
IB renderer_new_ib( const void * data = NULL, u32 len = 0, BufferUsage usage = BUFFERUSAGE_STATIC );
void renderer_delete_ib( IB ib );
-TB renderer_new_tb( TextureFormat format, const void * data = NULL, u32 len = 0, BufferUsage usage = BUFFERUSAGE_DYNAMIC );
-void renderer_tb_data( TB tb, const void * data, u32 len, BufferUsage usage = BUFFERUSAGE_DYNAMIC );
-void renderer_delete_tb( TB tb );
-
FB renderer_new_fb( TextureConfig texture_format, FramebufferAttachment attachment );
FB renderer_new_fb( const FramebufferConfig & config );
void renderer_delete_fb( FB fb );
-Shader renderer_new_shader( ShaderConfig config );
-Shader renderer_new_shader( const char * src );
+Shader renderer_new_shader( array< const char * > srcs );
void renderer_delete_shader( Shader shader );
Texture renderer_new_texture( TextureConfig config );
diff --git a/shaders.cc b/shaders.cc
@@ -8,10 +8,8 @@
struct HotloadShader {
const char * path = NULL;
- Shader shader = 0;
+ Shader shader = { };
time_t last_modified = 0;
- StaticArray< const char *, RENDERER_MAX_TEXTURES > texture_uniform_names = { };
- StaticArray< const char *, RENDERER_MAX_TEXTURE_BUFFERS > texture_buffer_uniform_names = { };
};
struct ShaderInclude {
@@ -37,58 +35,38 @@ void shaders_init() {
includes[ 1 ].path = "shaders/pbr.glsl";
shaders[ SHADER_TEXT ].path = "shaders/text.glsl";
- shaders[ SHADER_TEXT ].texture_uniform_names[ 0 ] = "atlas";
shaders[ SHADER_MSDF ].path = "shaders/msdf.glsl";
- shaders[ SHADER_MSDF ].texture_uniform_names[ 0 ] = "atlas";
shaders[ SHADER_SKYBOX ].path = "shaders/skybox.glsl";
- shaders[ SHADER_SKYBOX ].texture_uniform_names[ 0 ] = "blue_noise";
shaders[ SHADER_FLAT_VERTEX_COLOURS ].path = "shaders/flat_vertex_colours.glsl";
shaders[ SHADER_FLAT_TEXTURED ].path = "shaders/flat_textured.glsl";
- shaders[ SHADER_FLAT_TEXTURED ].texture_uniform_names[ 0 ] = "tex";
shaders[ SHADER_UI ].path = "shaders/ui.glsl";
shaders[ SHADER_TREE ].path = "shaders/tree.glsl";
- shaders[ SHADER_TREE ].texture_uniform_names[ 0 ] = "blue_noise";
shaders[ SHADER_WRITE_SHADOW_MAP ].path = "shaders/write_shadow_map.glsl";
shaders[ SHADER_DEBUG_RENDER_SHADOW_MAP ].path = "shaders/debug_render_shadow_map.glsl";
- shaders[ SHADER_DEBUG_RENDER_SHADOW_MAP ].texture_uniform_names[ 0 ] = "tex";
shaders[ SHADER_SHADOWED_VERTEX_COLOURS ].path = "shaders/shadowed_vertex_colours.glsl";
- shaders[ SHADER_SHADOWED_VERTEX_COLOURS ].texture_uniform_names[ 0 ] = "shadowmap";
- shaders[ SHADER_SHADOWED_VERTEX_COLOURS ].texture_uniform_names[ 1 ] = "dither_noise";
shaders[ SHADER_WIREFRAME ].path = "shaders/wireframe.glsl";
shaders[ SHADER_CLIPMAP ].path = "shaders/clipmap.glsl";
- shaders[ SHADER_CLIPMAP ].texture_uniform_names[ 0 ] = "heightmap";
- shaders[ SHADER_CLIPMAP ].texture_uniform_names[ 1 ] = "normalmap";
- shaders[ SHADER_CLIPMAP ].texture_uniform_names[ 2 ] = "horizonmap";
- shaders[ SHADER_CLIPMAP ].texture_uniform_names[ 3 ] = "blue_noise";
shaders[ SHADER_CLIPMAP_SKIRT ].path = "shaders/clipmap_skirt.glsl";
- shaders[ SHADER_CLIPMAP_SKIRT ].texture_uniform_names[ 0 ] = "blue_noise";
shaders[ SHADER_SHADOWED_VERTEX_COLOURS_TO_GBUFFER ].path = "shaders/shadowed_vertex_colours_to_gbuffer.glsl";
- shaders[ SHADER_SHADOWED_VERTEX_COLOURS_TO_GBUFFER ].texture_uniform_names[ 0 ] = "shadowmap";
- shaders[ SHADER_SHADOWED_VERTEX_COLOURS_TO_GBUFFER ].texture_uniform_names[ 1 ] = "dither_noise";
shaders[ SHADER_FLAT_VERTEX_COLOURS_TO_GBUFFER ].path = "shaders/flat_vertex_colours_to_gbuffer.glsl";
shaders[ SHADER_GBUFFER ].path = "shaders/gbuffer.glsl";
- shaders[ SHADER_GBUFFER ].texture_uniform_names[ 0 ] = "world_normal";
- shaders[ SHADER_GBUFFER ].texture_uniform_names[ 1 ] = "world_depth";
- shaders[ SHADER_GBUFFER ].texture_uniform_names[ 2 ] = "outline_depth";
- shaders[ SHADER_GBUFFER ].texture_uniform_names[ 3 ] = "blue_noise";
shaders[ SHADER_DEPTH_EDGE ].path = "shaders/depth_edge.glsl";
- shaders[ SHADER_DEPTH_EDGE ].texture_uniform_names[ 0 ] = "model_depth";
shaders[ SHADER_SKINNED_FLAT_VERTEX_COLOURS ].path = "shaders/skinned_flat_vertex_colours.glsl";
@@ -98,13 +76,13 @@ void shaders_init() {
}
}
-Shader get_shader( ShaderID id ) {
- return shaders[ id ].shader;
+const Shader * get_shader( ShaderID id ) {
+ return &shaders[ id ].shader;
}
void shaders_term() {
for( HotloadShader & shader : shaders ) {
- if( shader.shader != INVALID_SHADER ) {
+ if( shader.shader.program != 0 ) {
renderer_delete_shader( shader.shader );
}
}
@@ -123,21 +101,20 @@ static bool reload_shader( HotloadShader & shader, time_t t ) {
INFO( "loading {}", shader.path );
char * contents = ( char * ) file_get_contents( shader.path );
- ShaderConfig shader_config;
- shader_config.texture_uniform_names = shader.texture_uniform_names;
- shader_config.texture_buffer_uniform_names = shader.texture_buffer_uniform_names;
+ StaticArray< const char *, 8 > srcs;
size_t n = 0;
for( ShaderInclude & include : includes ) {
if( include.contents == NULL ) break;
- shader_config.srcs[ n ] = include.contents;
+ srcs[ n ] = include.contents;
n++;
}
- shader_config.srcs[ n ] = contents;
+ srcs[ n ] = contents;
+ n++;
- Shader new_shader = renderer_new_shader( shader_config );
+ Shader new_shader = renderer_new_shader( srcs.slice( 0, n ) );
- if( new_shader != INVALID_SHADER ) {
+ if( new_shader.program != 0 ) {
renderer_delete_shader( shader.shader );
shader.shader = new_shader;
}
diff --git a/shaders.h b/shaders.h
@@ -29,7 +29,7 @@ enum ShaderID {
};
void shaders_init();
-Shader get_shader( ShaderID id );
+const Shader * get_shader( ShaderID id );
void shaders_term();
int hotload_shaders();
diff --git a/shadow_map.cc b/shadow_map.cc
@@ -20,7 +20,7 @@ static v3 light_dir = v3_forward( -60, 90 );
static void draw_scene( RenderState render_state, bool draw_light = false ) {
m4 I = m4_identity();
- render_state.uniforms[ UNIFORMS_MODEL ] = renderer_uniforms( I );
+ render_state.set_uniform( "model", renderer_uniforms( I ) );
immediate_sphere( v3( 0, 0, 2 ), 2, v4( 1, 0, 0, 1 ) );
immediate_sphere( v3( -3, 7, 2 ), 2, v4( 0, 1, 0, 1 ) );
@@ -45,7 +45,7 @@ static void draw_scene( RenderState render_state, bool draw_light = false ) {
immediate_render( render_state );
m4 M = m4_translation( v3( -7, -2, 0 ) ) * m4_rotx( deg_to_rad( 90 ) );
- render_state.uniforms[ UNIFORMS_MODEL ] = renderer_uniforms( M );
+ render_state.set_uniform( "model", renderer_uniforms( M ) );
renderer_draw_mesh( tree_mesh, render_state );
}
@@ -132,7 +132,7 @@ GAME_FRAME( game_frame ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_WRITE_SHADOW_MAP );
- render_state.uniforms[ UNIFORMS_LIGHT_VIEW ] = light_view_uniforms;
+ render_state.set_uniform( "light_view", light_view_uniforms );
draw_scene( render_state );
@@ -145,7 +145,7 @@ GAME_FRAME( game_frame ) {
{
RenderState state;
state.shader = get_shader( SHADER_DEBUG_RENDER_SHADOW_MAP );
- state.textures[ 0 ] = shadow_fb.textures[ OUTPUT_DEPTH ];
+ state.set_texture( "tex", shadow_fb.textures[ OUTPUT_DEPTH ] );
renderer_draw_mesh( square, state );
}
@@ -153,10 +153,10 @@ GAME_FRAME( game_frame ) {
{
RenderState render_state;
render_state.shader = get_shader( SHADER_SHADOWED_VERTEX_COLOURS );
- render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( V, P, game->pos );
- render_state.uniforms[ UNIFORMS_LIGHT_VIEW ] = light_view_uniforms;
- render_state.textures[ 0 ] = shadow_fb.textures[ OUTPUT_DEPTH ];
- render_state.textures[ 1 ] = renderer_blue_noise();
+ render_state.set_uniform( "view", renderer_uniforms( V, P, game->pos ) );
+ render_state.set_uniform( "light_view", light_view_uniforms );
+ render_state.set_texture( "shadowmap", shadow_fb.textures[ OUTPUT_DEPTH ] );
+ render_state.set_texture( "dither_noise", renderer_blue_noise() );
draw_scene( render_state, true );
}
diff --git a/skybox.cc b/skybox.cc
@@ -121,9 +121,9 @@ void skybox_render( const Skybox * skybox, const m4 & Vsky, const m4 & P, float
RenderState render_state;
render_state.shader = get_shader( SHADER_SKYBOX );
- render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( Vsky, P );
- render_state.uniforms[ UNIFORMS_SKY ] = renderer_uniforms( hosek.A, hosek.B, hosek.C, hosek.D, hosek.E, hosek.F, hosek.G, hosek.H, hosek.I, hosek.Z, sun_dir );
- render_state.textures[ 0 ] = renderer_blue_noise();
+ render_state.set_uniform( "view", renderer_uniforms( Vsky, P ) );
+ render_state.set_uniform( "sky", renderer_uniforms( hosek.A, hosek.B, hosek.C, hosek.D, hosek.E, hosek.F, hosek.G, hosek.H, hosek.I, hosek.Z, sun_dir ) );
+ render_state.set_texture( "blue_noise", renderer_blue_noise() );
render_state.cull_face = CULLFACE_FRONT;
renderer_draw_mesh( skybox->mesh, render_state );
diff --git a/stringhash.h b/stringhash.h
@@ -2,13 +2,8 @@
#include "intrinsics.h"
-constexpr static inline u32 fnv1a( const char * str, size_t n ) {
- return n == 0 ? 0x811C9DC5 : ( fnv1a( str + 1, n - 1 ) ^ u32( u8( str[ 0 ] ) ) ) * 0x01000193;
-}
-
-template< size_t N >
-constexpr static inline u32 fnv1a( const char ( & str )[ N ] ) {
- return fnv1a( str, N );
+constexpr u32 fnv1a_ct( const char * str, size_t n, u32 basis = U32( 2166136261 ) ) {
+ return n == 0 ? basis : fnv1a_ct( str + 1, n - 1, ( basis ^ str[ 0 ] ) * U32( 16777619 ) );
}
struct StringHash {
@@ -20,7 +15,7 @@ struct StringHash {
template< size_t N >
StringHash( const char ( & s )[ N ] ) {
str = s;
- hash = fnv1a( s );
+ hash = fnv1a_ct( s, N - 1 );
}
};
diff --git a/text_renderer.cc b/text_renderer.cc
@@ -147,8 +147,8 @@ void draw_text( const char * str, int x, int y, float pixel_size ) {
RenderState render_state;
render_state.shader = get_shader( SHADER_TEXT );
- render_state.textures[ 0 ] = atlas;
- render_state.uniforms[ UNIFORMS_WINDOW ] = renderer_uniforms( get_window_size() );
+ render_state.set_uniform( "window", renderer_uniforms( get_window_size() ) );
+ render_state.set_texture( "atlas", atlas );
render_state.depth_func = DEPTHFUNC_DISABLED;
render_state.enable_alpha_blending = true;
// render_state.disable_depth_writes = true;