From 6b6f77f8af4ad9a1161985a6c9b09ac7eec9e10f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 5 Sep 2020 00:16:07 +0100 Subject: [PATCH] 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. --- src/world/format/Chunk.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/world/format/Chunk.php b/src/world/format/Chunk.php index d6ea71e83..cf11ad540 100644 --- a/src/world/format/Chunk.php +++ b/src/world/format/Chunk.php @@ -266,9 +266,38 @@ class Chunk{ * @phpstan-param \SplFixedArray $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; + } + } + } } } }