Chunk: improved heightmap calculation performance

recalculateHeightMapColumn is stateless, so it can't make any assumptions about which subchunks to check for blocks. However, in most the average case (6 allocated subchunks), this causes 2500+ useless SubChunk->getHighestBlockAt() calls (10 per column). Since we're calculating in bulk, we can figure out which subchunks are empty one time and ignore them for all 256 columns.
In the average case, this produced a 50-60% performance improvement for heightmap calculation (~1.1 ms -> 0.5 ms).
In extreme cases where the height is extremely varied, this produces no observable performance benefit, but for most cases with flattish terrain, it's an improvement.
It can likely be further improved, but further performance improvements are outside the scope of this commit and will likely result in more complexity increases.
This commit is contained in:
Dylan K. Taylor 2020-09-05 00:16:07 +01:00
parent e1816bd415
commit 6b6f77f8af

View File

@ -266,9 +266,38 @@ class Chunk{
* @phpstan-param \SplFixedArray<bool> $lightDiffusers
*/
public function recalculateHeightMap(\SplFixedArray $lightFilters, \SplFixedArray $lightDiffusers) : void{
$maxSubChunkY = $this->subChunks->count() - 1;
for(; $maxSubChunkY >= 0; $maxSubChunkY--){
if(!$this->getSubChunk($maxSubChunkY)->isEmptyFast()){
break;
}
}
if($maxSubChunkY === -1){ //whole column is definitely empty
$this->setHeightMapArray(array_fill(0, 256, 0));
return;
}
for($z = 0; $z < 16; ++$z){
for($x = 0; $x < 16; ++$x){
$this->recalculateHeightMapColumn($x, $z, $lightFilters, $lightDiffusers);
$y = null;
for($subChunkY = $maxSubChunkY; $subChunkY >= 0; $subChunkY--){
$subHighestBlockY = $this->getSubChunk($subChunkY)->getHighestBlockAt($x, $z);
if($subHighestBlockY !== -1){
$y = ($subChunkY * 16) + $subHighestBlockY;
break;
}
}
if($y === null){ //no blocks in the column
$this->setHeightMap($x, $z, 0);
}else{
for(; $y >= 0; --$y){
if($lightFilters[$state = $this->getFullBlock($x, $y, $z)] > 1 or $lightDiffusers[$state]){
$this->setHeightMap($x, $z, $y + 1);
break;
}
}
}
}
}
}