commit c7bebce9685ae788c8cda88bc84e9456d95668b8 parent 2ff05e63d22e1ea1e6c9fed6e94c24514d0f7040 Author: Michael Savage <mikejsavage@gmail.com> Date: Tue Dec 13 23:36:00 +0200 Make the terrain manager use an MPSC queue for tile loaded notifications Make the worker threads tell the main thread when they're done loading, instead of checking tiles every frame for updates. Diffstat:
terrain_manager.cc | | | 82 | ++++++++++++++++++++++++++++++++++--------------------------------------------- |
terrain_manager.h | | | 15 | +++++++++------ |
diff --git a/terrain_manager.cc b/terrain_manager.cc @@ -10,6 +10,7 @@ #include "gpubtt.h" #include "linear_algebra.h" #include "str.h" +#include "nonblocking_fixed_mpsc_queue.h" #include "lz4.h" @@ -83,7 +84,7 @@ static const char * frag_src = GLSL( vec3 sun_direction = normalize( vec3( -1, 0, sun ) ); float sunlight_lambert = max( 0, dot( normal, sun_direction ) ); vec3 sunlight = sun_visible_fraction * sunlight_lambert * vec3( 0.9, 0.9, 0.5 ); - vec3 ambient = vec3( 0.02, 0.02, 0.15 ); + vec3 ambient = vec3( 0.05, 0.05, 0.15 ); vec3 c = ( sunlight + ambient ) * ground; @@ -189,7 +190,8 @@ static WORK_QUEUE_CALLBACK( async_load_tile ) { tm->gpu_tiles[ tx ][ ty ] = gpu_tile_data; // mark as ready to upload - store_release( &tm->tile_states[ tx ][ ty ], TILE_READY_FOR_UPLOAD ); + ReadyTile ready_tile = { tx, ty }; + tm->ready_tiles.enqueue_spin( ready_tile ); } static bool terrain_tile_inside_world( const TerrainManager * tm, s32 tx, s32 ty ) { @@ -202,11 +204,13 @@ static bool terrain_tile_inside_world( const TerrainManager * tm, s32 tx, s32 ty static void terrain_load_tile( TerrainManager * tm, s32 tx, s32 ty ) { if( !terrain_tile_inside_world( tm, tx, ty ) ) return; - if( load_acquire( &tm->tile_states[ tx ][ ty ] ) != TILE_EMPTY ) { + if( tm->tile_states[ tx ][ ty ] != TILE_EMPTY ) { return; } AsyncLoadTile * alt = ( AsyncLoadTile * ) malloc( sizeof( AsyncLoadTile ) ); + if( alt == NULL ) return; + alt->tm = tm; alt->tx = tx; alt->ty = ty; @@ -216,19 +220,17 @@ static void terrain_load_tile( TerrainManager * tm, s32 tx, s32 ty ) { static void terrain_unload_tile( TerrainManager * tm, s32 tx, s32 ty ) { if( !terrain_tile_inside_world( tm, tx, ty ) ) return; - if( load_acquire( &tm->tile_states[ tx ][ ty ] ) != TILE_ON_GPU ) { + if( tm->tile_states[ tx ][ ty ] != TILE_LOADED ) { return; } - store_release( &tm->tile_states[ tx ][ ty ], TILE_FREEING ); - printf( "destroyed %d %d\n", tx, ty ); heightmap_destroy( &tm->decompressed_tiles[ tx ][ ty ].heightmap ); free( tm->decompressed_tiles[ tx ][ ty ].normalmap ); free( tm->decompressed_tiles[ tx ][ ty ].quadtree.nodes_memory ); gpubtt_destroy( &tm->gpubtts[ tx ][ ty ] ); - store_release( &tm->tile_states[ tx ][ ty ], TILE_EMPTY ); + tm->tile_states[ tx ][ ty ] = TILE_EMPTY; } void terrain_init( @@ -274,7 +276,7 @@ void terrain_init( compressed_tile.quadtree_data = quadtree; compressed_tile.quadtree_size = quadtree_size; - store_release( &tm->tile_states[ tx ][ ty ], TILE_EMPTY ); + tm->tile_states[ tx ][ ty ] = TILE_EMPTY; } } @@ -409,35 +411,26 @@ struct FSData { }; void terrain_render( TerrainManager * tm, const m4 & V, const m4 & P, float sun, double current_time ) { - u32 inited = 0; - for( u16 vy = 0; vy < VIEW_SIZE; vy++ ) { - for( u16 vx = 0; vx < VIEW_SIZE; vx++ ) { - s32 tx = vx + tm->tile_x - VIEW_HALF; - s32 ty = vy + tm->tile_y - VIEW_HALF; - - if( tx < 0 || ty < 0 ) continue; - - u32 state = load_acquire( &tm->tile_states[ tx ][ ty ] ); - if( state == TILE_READY_FOR_UPLOAD ) { - if( inited > 1 ) continue; - const GPUTileData & gpu_tile_data = tm->gpu_tiles[ tx ][ ty ]; - // TODO: move this into terrain_prepare_render? - const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; - const OffsetHeightmap ohm = { *hm, checked_cast< float >( tx * TILE_SIZE ), checked_cast< float >( ty * TILE_SIZE ) }; - gpubtt_init( - tm->arena, - &tm->gpubtts[ tx ][ ty ], - gpu_tile_data.btt, - &ohm, - tm->decompressed_tiles[ tx ][ ty ].normalmap, - gpu_tile_data.horizonmap - ); - free( gpu_tile_data.btt_memory ); - free( gpu_tile_data.horizonmap ); - store_release( &tm->tile_states[ tx ][ ty ], TILE_ON_GPU ); - inited++; - } - } + ReadyTile ready_tile; + while( tm->ready_tiles.dequeue( &ready_tile ) ) { + s32 tx = ready_tile.tx; + s32 ty = ready_tile.ty; + + const GPUTileData & gpu_tile_data = tm->gpu_tiles[ tx ][ ty ]; + const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; + const OffsetHeightmap ohm = { *hm, checked_cast< float >( tx * TILE_SIZE ), checked_cast< float >( ty * TILE_SIZE ) }; + gpubtt_init( + tm->arena, + &tm->gpubtts[ tx ][ ty ], + gpu_tile_data.btt, + &ohm, + tm->decompressed_tiles[ tx ][ ty ].normalmap, + gpu_tile_data.horizonmap + ); + free( gpu_tile_data.btt_memory ); + free( gpu_tile_data.horizonmap ); + + tm->tile_states[ tx ][ ty ] = TILE_LOADED; } VSData vsdata = { V, P }; @@ -480,8 +473,7 @@ void terrain_render( TerrainManager * tm, const m4 & V, const m4 & P, float sun, if( tx < 0 || ty < 0 ) continue; - u32 state = load_acquire( &tm->tile_states[ tx ][ ty ] ); - if( state == TILE_ON_GPU ) { + if( tm->tile_states[ tx ][ ty ] == TILE_LOADED ) { gpubtt_render( &tm->gpubtts[ tx ][ ty ], render_state ); } } @@ -497,13 +489,11 @@ float terrain_height( const TerrainManager * tm, v3 position ) { s32 tx = position.x / TILE_SIZE; s32 ty = position.y / TILE_SIZE; - TileState tile_state = checked_cast< TileState >( load_acquire( &tm->tile_states[ tx ][ ty ] ) ); - if( tile_state != TILE_READY_FOR_UPLOAD && tile_state != TILE_ON_GPU ) { + if( tm->tile_states[ tx ][ ty ] != TILE_LOADED ) { return 0.0f; } const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; - return hm->bilerp_height( position.x - tx * TILE_SIZE, position.y - ty * TILE_SIZE ); } @@ -543,12 +533,11 @@ v3 terrain_normal( const TerrainManager * tm, v3 position ) { s32 tx = position.x / TILE_SIZE; s32 ty = position.y / TILE_SIZE; - TileState tile_state = checked_cast< TileState >( load_acquire( &tm->tile_states[ tx ][ ty ] ) ); - if( tile_state != TILE_READY_FOR_UPLOAD && tile_state != TILE_ON_GPU ) { + if( tm->tile_states[ tx ][ ty ] != TILE_LOADED ) { return v3( 0, 0, 1 ); } - const array2d< v3 > normalmap( tm->decompressed_tiles[ tx ][ ty ].normalmap, TILE_SIZE, TILE_SIZE ); + const array2d< v3 > normalmap( tm->decompressed_tiles[ tx ][ ty ].normalmap, TILE_SIZE + 1, TILE_SIZE + 1 ); float local_x = position.x - tx * TILE_SIZE; float local_y = position.y - ty * TILE_SIZE; @@ -571,8 +560,7 @@ bool segment_vs_terrain( const TerrainManager * tm, v3 seg_origin, v3 seg_end, f if( tx < 0 || ty < 0 ) continue; - TileState tile_state = checked_cast< TileState >( load_acquire( &tm->tile_states[ tx ][ ty ] ) ); - if( tile_state != TILE_READY_FOR_UPLOAD && tile_state != TILE_ON_GPU ) { + if( tm->tile_states[ tx ][ ty ] != TILE_LOADED ) { continue; } diff --git a/terrain_manager.h b/terrain_manager.h @@ -7,8 +7,8 @@ #include "heightmap.h" #include "gpubtt.h" #include "work_queue.h" -#include "platform_atomic.h" #include "linear_algebra.h" +#include "nonblocking_fixed_mpsc_queue.h" static const u16 TILE_SIZE = 512; static const u16 WORLD_SIZE = 64; @@ -44,9 +44,11 @@ struct DecompressedTile { enum TileState { TILE_EMPTY, TILE_LOADING, - TILE_READY_FOR_UPLOAD, - TILE_ON_GPU, - TILE_FREEING, + TILE_LOADED, +}; + +struct ReadyTile { + s32 tx, ty; }; struct TerrainManager { @@ -66,9 +68,8 @@ struct TerrainManager { s32 tile_x, tile_y; CompressedTile compressed_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; - array2d< CompressedTile > ASDF = array2d< CompressedTile >( *compressed_tiles, WORLD_SIZE, WORLD_SIZE ); DecompressedTile decompressed_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; - atomic_u32 tile_states[ WORLD_SIZE ][ WORLD_SIZE ]; + TileState tile_states[ WORLD_SIZE ][ WORLD_SIZE ]; // holds tile data that needs to be moved to the gpu GPUTileData gpu_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; @@ -77,6 +78,8 @@ struct TerrainManager { GPUBTT gpubtts[ WORLD_SIZE ][ WORLD_SIZE ]; u8 lods[ WORLD_SIZE ][ WORLD_SIZE ]; + + NonblockingFixedMPSCQueue< ReadyTile, 32 > ready_tiles; }; void terrain_init( TerrainManager * tm, const char * tiles_dir, MemoryArena * arena, WorkQueue * background_tasks );