commit 4025cf32a4e4d65d1235bf987c005c447537343f
parent b82bc4149f4e6bfa70ae20f9ddef928fdf44d904
Author: Michael Savage <mikejsavage@gmail.com>
Date: Mon, 5 Aug 2019 20:28:32 +0300
Add multisample framebuffers and MSAA resolve pass
Diffstat:
M | clipmap.cc | | | 40 | +++++++++++++++++++++++++++++++++++++--- |
M | renderer.cc | | | 383 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
M | renderer.h | | | 10 | +++++++--- |
3 files changed, 246 insertions(+), 187 deletions(-)
diff --git a/clipmap.cc b/clipmap.cc
@@ -56,6 +56,8 @@ struct Player {
v3 pos;
};
+static FB msaa_fb;
+
static UDPSocket sock;
static NetAddress server_addr;
static u64 sid;
@@ -123,6 +125,8 @@ struct RGBA {
GAME_INIT( game_init ) {
net_init();
+ msaa_fb = { };
+
// load quadtree
{
clipmap.heightmap = alloc_array2d< u16 >( &mem->persistent_arena, 4096, 4096 );
@@ -561,7 +565,36 @@ static bool intervals_overlap( float a0, float a1, float b0, float b1 ) {
return a0 <= b1 && b0 <= a1;
}
+static void recreate_framebuffers() {
+ renderer_delete_fb( msaa_fb );
+
+ v2u32 window_size = get_window_size();
+
+ TextureConfig texture_config;
+ texture_config.width = window_size.x;
+ texture_config.height = window_size.y;
+ texture_config.wrap = TEXWRAP_CLAMP;
+
+ FramebufferConfig fb_config;
+
+ texture_config.format = TEXFMT_RGB_U8_SRGB;
+ fb_config.textures[ OUTPUT_ALBEDO ].config = texture_config;
+ fb_config.textures[ OUTPUT_ALBEDO ].attachment = FB_COLOUR;
+
+ texture_config.format = TEXFMT_DEPTH;
+ fb_config.textures[ OUTPUT_DEPTH ].config = texture_config;
+ fb_config.textures[ OUTPUT_DEPTH ].attachment = FB_DEPTH;
+
+ fb_config.msaa_samples = 0;
+
+ msaa_fb = renderer_new_fb( fb_config );
+}
+
GAME_FRAME( game_frame ) {
+ if( input->resized ) {
+ recreate_framebuffers();
+ }
+
float fb = float( input->keys[ KEY_W ] - input->keys[ KEY_S ] );
float lr = float( input->keys[ KEY_D ] - input->keys[ KEY_A ] );
float dz = float( input->keys[ KEY_SPACE ] - input->keys[ KEY_LEFTSHIFT ] );
@@ -756,9 +789,10 @@ GAME_FRAME( game_frame ) {
renderer_begin_frame();
- u8 world_pass = renderer_add_pass( "Render world", RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO );
- u8 ents_pass = renderer_add_pass( "Render entities" );
- u8 sky_pass = renderer_add_pass( "Render sky" );
+ u8 world_pass = renderer_add_pass( "Render world", msaa_fb, RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO );
+ u8 ents_pass = renderer_add_pass( "Render entities", msaa_fb );
+ u8 sky_pass = renderer_add_pass( "Render sky", msaa_fb );
+ renderer_resolve_msaa( msaa_fb );
u8 ui_pass = renderer_add_pass( "Render UI" );
UniformBinding view_uniforms = renderer_uniforms( V, P, game->pos );
diff --git a/renderer.cc b/renderer.cc
@@ -377,7 +377,14 @@ void renderer_end_frame() {
pass_idx = dc.render_state.pass;
}
- if( dc.num_instances == 0 ) {
+ if( dc.render_state.shader == NULL ) {
+ FB fb = render_passes[ dc.render_state.pass ].msaa_source;
+ v2u32 window_size = get_window_size();
+ ASSERT( fb.width == window_size.x && fb.height == window_size.y );
+ glBindFramebuffer( GL_READ_FRAMEBUFFER, fb.fbo );
+ glBlitFramebuffer( 0, 0, fb.width, fb.height, 0, 0, fb.width, fb.height, GL_COLOR_BUFFER_BIT, GL_NEAREST );
+ }
+ else if( dc.num_instances == 0 ) {
drawcall_single( dc.mesh, dc.render_state );
}
else {
@@ -444,6 +451,20 @@ u8 renderer_add_pass( const char * name, ClearColourBool clear_colour, ClearDept
return renderer_add_pass( name, target, clear_colour, clear_depth );
}
+void renderer_resolve_msaa( FB fb ) {
+ RenderPass pass;
+ pass.name = "Resolve MSAA";
+ pass.msaa_source = fb;
+
+ RenderState dummy;
+ dummy.pass = renderer_add_pass( pass );
+ dummy.shader = NULL;
+
+ DrawCall dc;
+ dc.render_state = dummy;
+ draw_calls.add( dc );
+}
+
u32 renderer_num_draw_calls() {
return draw_calls_this_frame;
}
@@ -617,6 +638,178 @@ void renderer_delete_ib( IB ib ) {
glDeleteBuffers( 1, &ib );
}
+static u32 mipmap_dim( u32 dim, u32 level ) {
+ return max( dim >> level, u32( 1 ) );
+}
+
+// TODO: use clz
+static u32 mipmap_levels( u32 width, u32 height ) {
+ u32 levels = 1;
+ u32 dim = max( width, height );
+ while( dim > 1 ) {
+ dim >>= 1;
+ levels++;
+ }
+ return levels;
+}
+
+static u32 texture_byte_size( u32 width, u32 height, TextureFormat format, bool mipmaps ) {
+ u32 total_dim = width * height;
+ if( mipmaps ) {
+ for( u32 i = 1; i < mipmap_levels( width, height ); i++ ) {
+ total_dim += mipmap_dim( width, i ) * mipmap_dim( height, i );
+ }
+ }
+
+ switch( format ) {
+ case TEXFMT_INVALID:
+ case TEXFMT_DEPTH:
+ break;
+
+ case TEXFMT_R_U8:
+ case TEXFMT_R_U8NORM:
+ return total_dim * 1 * sizeof( u8 );
+ case TEXFMT_R_U16:
+ return total_dim * 1 * sizeof( u16 );
+ case TEXFMT_R_FLOAT:
+ return total_dim * 1 * sizeof( float );
+
+ case TEXFMT_RGB_U8:
+ return total_dim * 3 * sizeof( u8 );
+ case TEXFMT_RGB_U8_SRGB:
+ return total_dim * 3 * sizeof( u8 );
+ case TEXFMT_RGB_HALF:
+ return total_dim * 3 * sizeof( u16 );
+ case TEXFMT_RGB_FLOAT:
+ return total_dim * 3 * sizeof( float );
+
+ case TEXFMT_RGBA_U8:
+ return total_dim * 4 * sizeof( u8 );
+ case TEXFMT_RGBA_U8_SRGB:
+ return total_dim * 4 * sizeof( u8 );
+ case TEXFMT_RGBA_FLOAT:
+ return total_dim * 4 * sizeof( float );
+
+ case TEXFMT_BC1:
+ case TEXFMT_BC1_SRGB:
+ return total_dim / 2;
+ case TEXFMT_BC4:
+ return total_dim / 2;
+ case TEXFMT_BC5:
+ return total_dim;
+ }
+
+ FATAL( "unsupported TextureFormat: {}", format );
+ return GL_INVALID_ENUM;
+}
+
+static Texture new_texture( TextureConfig config, int msaa_samples ) {
+ ASSERT( config.format != TEXFMT_INVALID );
+ ASSERT( !is_compressed( config.format ) || msaa_samples == 0 );
+ ASSERT( !config.has_mipmaps ); // TODO
+
+ GLenum target = msaa_samples == 0 ? GL_TEXTURE_2D : GL_TEXTURE_2D_MULTISAMPLE;
+
+ GLuint texture;
+ glGenTextures( 1, &texture );
+ // TODO: should probably update the gl state since we bind a new texture
+ glBindTexture( target, texture );
+
+ if( msaa_samples == 0 ) {
+ glTexParameteri( target, GL_TEXTURE_WRAP_S, texturewrap_to_glenum( config.wrap ) );
+ glTexParameteri( target, GL_TEXTURE_WRAP_T, texturewrap_to_glenum( config.wrap ) );
+
+ GLenum min_filter, mag_filter;
+ texturefilter_to_glenum( config.filter, config.has_mipmaps, &min_filter, &mag_filter );
+ glTexParameteri( target, GL_TEXTURE_MIN_FILTER, min_filter );
+ glTexParameteri( target, GL_TEXTURE_MAG_FILTER, mag_filter );
+
+ if( config.wrap == TEXWRAP_BORDER ) {
+ glTexParameterfv( target, GL_TEXTURE_BORDER_COLOR, ( GLfloat * ) &config.border_colour );
+ }
+ }
+
+ GLenum internal_format = texfmt_to_internal_format( config.format );
+
+ if( is_compressed( config.format ) ) {
+ u32 dxt_size = texture_byte_size( config.width, config.height, config.format, false );
+ glCompressedTexImage2D( GL_TEXTURE_2D, 0, internal_format,
+ config.width, config.height, 0, dxt_size, config.data );
+ }
+ else {
+ GLenum channels = GL_INVALID_ENUM;
+ GLenum type = GL_INVALID_ENUM;
+
+ switch( config.format ) {
+ case TEXFMT_R_U8:
+ case TEXFMT_R_U8NORM:
+ channels = GL_RED;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ case TEXFMT_R_U16:
+ channels = GL_RED;
+ type = GL_UNSIGNED_SHORT;
+ break;
+ case TEXFMT_R_FLOAT:
+ channels = GL_RED;
+ type = GL_FLOAT;
+ break;
+
+ case TEXFMT_RGB_U8:
+ case TEXFMT_RGB_U8_SRGB:
+ channels = GL_RGB;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ case TEXFMT_RGB_HALF:
+ channels = GL_RGB;
+ type = GL_HALF_FLOAT;
+ break;
+ case TEXFMT_RGB_FLOAT:
+ channels = GL_RGB;
+ type = GL_FLOAT;
+ break;
+
+ case TEXFMT_RGBA_U8:
+ case TEXFMT_RGBA_U8_SRGB:
+ channels = GL_RGBA;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ case TEXFMT_RGBA_FLOAT:
+ channels = GL_RGBA;
+ type = GL_FLOAT;
+ break;
+
+ case TEXFMT_DEPTH:
+ channels = GL_DEPTH_COMPONENT;
+ type = GL_FLOAT;
+ break;
+
+ default:
+ FATAL( "implement channel/type selection for {}", config.format );
+ break;
+ }
+
+ if( msaa_samples == 0 ) {
+ glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
+ config.width, config.height, 0, channels, type, config.data );
+ }
+ else {
+ glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, msaa_samples,
+ internal_format, config.width, config.height, GL_TRUE );
+ }
+ }
+
+ return texture;
+}
+
+Texture renderer_new_texture( const TextureConfig & config ) {
+ return new_texture( config, 0 );
+}
+
+void renderer_delete_texture( Texture texture ) {
+ glDeleteTextures( 1, &texture );
+}
+
static GLenum framebufferattachment_to_glenum( FramebufferAttachment attachment ) {
switch( attachment ) {
case FB_COLOUR: return GL_COLOR_ATTACHMENT0;
@@ -650,8 +843,10 @@ FB renderer_new_fb( const FramebufferConfig & config ) {
ASSERT( !is_compressed( texture_config.format ) );
GLenum attachment = framebufferattachment_to_glenum( config.textures[ i ].attachment );
- Texture texture = renderer_new_texture( texture_config );
- glFramebufferTexture2D( GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture, 0 );
+ Texture texture = new_texture( texture_config, config.msaa_samples );
+
+ GLenum target = config.msaa_samples == 0 ? GL_TEXTURE_2D : GL_TEXTURE_2D_MULTISAMPLE;
+ glFramebufferTexture2D( GL_FRAMEBUFFER, attachment, target, texture, 0 );
width = texture_config.width;
height = texture_config.height;
@@ -673,28 +868,10 @@ FB renderer_new_fb( const FramebufferConfig & config ) {
}
FB renderer_new_fb( TextureConfig texture_config, FramebufferAttachment attachment ) {
- ASSERT( !is_compressed( texture_config.format ) );
-
- Texture texture = renderer_new_texture( texture_config );
- GLuint fbo;
-
- glGenFramebuffers( 1, &fbo );
- glBindFramebuffer( GL_FRAMEBUFFER, fbo );
- glFramebufferTexture2D( GL_FRAMEBUFFER, framebufferattachment_to_glenum( attachment ), GL_TEXTURE_2D, texture, 0 );
-
- if( attachment == FB_DEPTH ) {
- glDrawBuffer( GL_NONE );
- }
-
- ASSERT( glCheckFramebufferStatus( GL_FRAMEBUFFER ) == GL_FRAMEBUFFER_COMPLETE );
- glBindFramebuffer( GL_FRAMEBUFFER, 0 );
-
- FB fb;
- fb.fbo = fbo;
- fb.textures[ attachment == FB_COLOUR ? OUTPUT_ALBEDO : OUTPUT_DEPTH ] = texture;
- fb.width = texture_config.width;
- fb.height = texture_config.height;
- return fb;
+ FramebufferConfig config;
+ config.textures[ 0 ].config = texture_config;
+ config.textures[ 0 ].attachment = attachment;
+ return renderer_new_fb( config );
}
void renderer_delete_fb( FB fb ) {
@@ -829,162 +1006,6 @@ void renderer_delete_shader( Shader shader ) {
glDeleteProgram( shader.program );
}
-static u32 mipmap_dim( u32 dim, u32 level ) {
- return max( dim >> level, u32( 1 ) );
-}
-
-// TODO: use clz
-static u32 mipmap_levels( u32 width, u32 height ) {
- u32 levels = 1;
- u32 dim = max( width, height );
- while( dim > 1 ) {
- dim >>= 1;
- levels++;
- }
- return levels;
-}
-
-static u32 texture_byte_size( u32 width, u32 height, TextureFormat format, bool mipmaps ) {
- u32 total_dim = width * height;
- if( mipmaps ) {
- for( u32 i = 1; i < mipmap_levels( width, height ); i++ ) {
- total_dim += mipmap_dim( width, i ) * mipmap_dim( height, i );
- }
- }
-
- switch( format ) {
- case TEXFMT_INVALID:
- case TEXFMT_DEPTH:
- break;
-
- case TEXFMT_R_U8:
- case TEXFMT_R_U8NORM:
- return total_dim * 1 * sizeof( u8 );
- case TEXFMT_R_U16:
- return total_dim * 1 * sizeof( u16 );
- case TEXFMT_R_FLOAT:
- return total_dim * 1 * sizeof( float );
-
- case TEXFMT_RGB_U8:
- return total_dim * 3 * sizeof( u8 );
- case TEXFMT_RGB_U8_SRGB:
- return total_dim * 3 * sizeof( u8 );
- case TEXFMT_RGB_HALF:
- return total_dim * 3 * sizeof( u16 );
- case TEXFMT_RGB_FLOAT:
- return total_dim * 3 * sizeof( float );
-
- case TEXFMT_RGBA_U8:
- return total_dim * 4 * sizeof( u8 );
- case TEXFMT_RGBA_U8_SRGB:
- return total_dim * 4 * sizeof( u8 );
- case TEXFMT_RGBA_FLOAT:
- return total_dim * 4 * sizeof( float );
-
- case TEXFMT_BC1:
- case TEXFMT_BC1_SRGB:
- return total_dim / 2;
- case TEXFMT_BC4:
- return total_dim / 2;
- case TEXFMT_BC5:
- return total_dim;
- }
-
- FATAL( "unsupported TextureFormat: {}", format );
- return GL_INVALID_ENUM;
-}
-
-Texture renderer_new_texture( TextureConfig config ) {
- ASSERT( config.format != TEXFMT_INVALID );
- ASSERT( !config.has_mipmaps ); // TODO
-
- GLuint texture;
- glGenTextures( 1, &texture );
- // TODO: should probably update the gl state since we bind a new texture
- glBindTexture( GL_TEXTURE_2D, texture );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texturewrap_to_glenum( config.wrap ) );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texturewrap_to_glenum( config.wrap ) );
-
- GLenum min_filter, mag_filter;
- texturefilter_to_glenum( config.filter, config.has_mipmaps, &min_filter, &mag_filter );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter );
- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter );
-
- if( config.wrap == TEXWRAP_BORDER ) {
- glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, ( GLfloat * ) &config.border_colour );
- }
-
- GLenum internal_format = texfmt_to_internal_format( config.format );
-
- if( is_compressed( config.format ) ) {
- u32 dxt_size = texture_byte_size( config.width, config.height, config.format, false );
- glCompressedTexImage2D( GL_TEXTURE_2D, 0, internal_format,
- config.width, config.height, 0, dxt_size, config.data );
- }
- else {
- GLenum channels = GL_INVALID_ENUM;
- GLenum type = GL_INVALID_ENUM;
-
- switch( config.format ) {
- case TEXFMT_R_U8:
- case TEXFMT_R_U8NORM:
- channels = GL_RED;
- type = GL_UNSIGNED_BYTE;
- break;
- case TEXFMT_R_U16:
- channels = GL_RED;
- type = GL_UNSIGNED_SHORT;
- break;
- case TEXFMT_R_FLOAT:
- channels = GL_RED;
- type = GL_FLOAT;
- break;
-
- case TEXFMT_RGB_U8:
- case TEXFMT_RGB_U8_SRGB:
- channels = GL_RGB;
- type = GL_UNSIGNED_BYTE;
- break;
- case TEXFMT_RGB_HALF:
- channels = GL_RGB;
- type = GL_HALF_FLOAT;
- break;
- case TEXFMT_RGB_FLOAT:
- channels = GL_RGB;
- type = GL_FLOAT;
- break;
-
- case TEXFMT_RGBA_U8:
- case TEXFMT_RGBA_U8_SRGB:
- channels = GL_RGBA;
- type = GL_UNSIGNED_BYTE;
- break;
- case TEXFMT_RGBA_FLOAT:
- channels = GL_RGBA;
- type = GL_FLOAT;
- break;
-
- case TEXFMT_DEPTH:
- channels = GL_DEPTH_COMPONENT;
- type = GL_FLOAT;
- break;
-
- default:
- FATAL( "implement channel/type selection for {}", config.format );
- break;
- }
-
- glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
- config.width, config.height, 0, channels, type, config.data );
- }
-
- return texture;
-}
-
-void renderer_delete_texture( Texture texture ) {
- glDeleteTextures( 1, &texture );
-}
-
static void vertexfmt_to_glenum( VertexFormat format, GLenum * type, int * components ) {
switch( format ) {
case VERTEXFMT_U8x3:
diff --git a/renderer.h b/renderer.h
@@ -275,6 +275,8 @@ struct RenderPass {
bool clear_depth = false;
float depth = 1.0f;
+
+ FB msaa_source = { };
};
struct FramebufferConfig {
@@ -284,6 +286,7 @@ struct FramebufferConfig {
};
StaticArray< FramebufferTexture, OUTPUTS_COUNT > textures = { };
+ int msaa_samples = 0;
};
enum ClearColourBool { RENDERER_CLEAR_COLOUR_DONT, RENDERER_CLEAR_COLOUR_DO };
@@ -298,6 +301,7 @@ void renderer_end_frame();
u8 renderer_add_pass( const RenderPass & config );
u8 renderer_add_pass( const char * name, ClearColourBool clear_colour = RENDERER_CLEAR_COLOUR_DONT, ClearDepthBool clear_depth = RENDERER_CLEAR_DEPTH_DONT );
u8 renderer_add_pass( const char * name, FB target, ClearColourBool clear_colour = RENDERER_CLEAR_COLOUR_DONT, ClearDepthBool clear_depth = RENDERER_CLEAR_DEPTH_DONT );
+void renderer_resolve_msaa( FB fb );
u32 renderer_num_draw_calls();
u32 renderer_num_vertices();
@@ -312,6 +316,9 @@ 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 );
+Texture renderer_new_texture( const TextureConfig & config );
+void renderer_delete_texture( Texture texture );
+
FB renderer_new_fb( TextureConfig texture_format, FramebufferAttachment attachment );
FB renderer_new_fb( const FramebufferConfig & config );
void renderer_delete_fb( FB fb );
@@ -319,9 +326,6 @@ void renderer_delete_fb( FB fb );
Shader renderer_new_shader( array< const char * > srcs );
void renderer_delete_shader( Shader shader );
-Texture renderer_new_texture( TextureConfig config );
-void renderer_delete_texture( Texture texture );
-
Mesh renderer_new_mesh( MeshConfig config );
void renderer_draw_mesh( const Mesh & mesh, const RenderState & state );
void renderer_draw_instances( const Mesh & mesh, const RenderState & state, u32 num_instances, VB instace_data );