Skip to content

Commit

Permalink
Fix performance regression in Entity#updateFluidHeightAndDoFluidPushing
Browse files Browse the repository at this point in the history
The upper bounds from Vanilla are exclusive, but we were treating
them as inclusive which changes behavior and increases the number
of blocks read.
  • Loading branch information
Spottedleaf committed Sep 30, 2024
1 parent a8d4ce5 commit a4770ac
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, fin
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
final int minBlockZ = Mth.floor(boundingBox.minZ);

final int maxBlockX = Mth.ceil(boundingBox.maxX);
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY));
final int maxBlockZ = Mth.ceil(boundingBox.maxZ);
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;

final boolean isPushable = this.isPushedByFluid();
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
Expand All @@ -92,9 +93,7 @@ public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, fin

for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);

final LevelChunkSection[] sections = chunk.getSections();
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();

// bound y
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
Expand All @@ -118,23 +117,17 @@ public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, fin
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;

for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4);
mutablePos.setY(blockY);
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
final int blockZ = currZ | (currChunkZ << 4);
mutablePos.setZ(blockZ);
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
final int blockX = currX | (currChunkX << 4);
mutablePos.setX(blockX);

final FluidState fluidState = blocks.get(localBlockIndex).getFluidState();
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();

if (fluidState.isEmpty() || !fluidState.is(fluid)) {
continue;
}

final double height = (double)((float)blockY + fluidState.getHeight(world, mutablePos));
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));

final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
final double diff = height - boundingBox.minY;

if (diff < 0.0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ public void updateFluidHeightAndDoFluidPushing() {
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
final int minBlockZ = Mth.floor(boundingBox.minZ);

final int maxBlockX = Mth.ceil(boundingBox.maxX);
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY));
final int maxBlockZ = Mth.ceil(boundingBox.maxZ);
// note: bounds are exclusive in Vanilla, so we subtract 1
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
final int maxBlockY = Math.min((((GetBlockLevel)world).moonrise$getMaxSection() << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;

final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();

Expand All @@ -86,9 +87,7 @@ public void updateFluidHeightAndDoFluidPushing() {

for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);

final LevelChunkSection[] sections = chunk.getSections();
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();

// bound y
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
Expand All @@ -112,22 +111,16 @@ public void updateFluidHeightAndDoFluidPushing() {
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;

for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4);
mutablePos.setY(blockY);
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
final int blockZ = currZ | (currChunkZ << 4);
mutablePos.setZ(blockZ);
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
final int blockX = currX | (currChunkX << 4);
mutablePos.setX(blockX);

final FluidState fluidState = blocks.get(localBlockIndex).getFluidState();
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();

if (fluidState.isEmpty()) {
continue;
}

mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));

final FluidType type = fluidState.getFluidType();

// note: assume fluidState.isEmpty() == type.isAir()
Expand All @@ -136,7 +129,7 @@ public void updateFluidHeightAndDoFluidPushing() {
return new FluidPushCalculation();
});

final double height = (double)((float)blockY + fluidState.getHeight(world, mutablePos));
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
final double diff = height - boundingBox.minY;

if (diff < 0.0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ public boolean hasChunk(final int x, final int z) {
return this.getChunkSource().hasChunk(x, z);
}

@Override
public boolean hasChunksAt(final int minBlockX, final int minBlockZ, final int maxBlockX, final int maxBlockZ) {
return this.moonrise$areChunksLoaded(
minBlockX >> 4, minBlockZ >> 4, maxBlockX >> 4, maxBlockZ >> 4
);
}

/**
* @reason Turn all getChunk(x, z, status) calls into virtual invokes, instead of interface invokes:
* 1. The interface invoke is expensive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ public boolean isInWall() {

for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true);

final LevelChunkSection[] sections = chunk.getSections();
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, true).getSections();

for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
Expand Down Expand Up @@ -259,11 +257,10 @@ public boolean isInWall() {
final int blockZ = currZ | (currChunkZ << 4);
mutablePos.setZ(blockZ);
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
final int blockX = currX | (currChunkX << 4);
mutablePos.setX(blockX);

final BlockState blockState = blocks.get(localBlockIndex);
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));

if (((CollisionBlockState)blockState).moonrise$emptyCollisionShape()
|| !blockState.isSuffocating(world, mutablePos)) {
Expand Down Expand Up @@ -336,13 +333,10 @@ private <T> Stream<T> avoidStreams(final Level world, final AABB boundingBox) {

final int minSection = ((GetBlockLevel)world).moonrise$getMinSection();
final ChunkSource chunkSource = world.getChunkSource();
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();

for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);

final LevelChunkSection[] sections = chunk.getSections();
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();

for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
Expand All @@ -365,17 +359,9 @@ private <T> Stream<T> avoidStreams(final Level world, final AABB boundingBox) {
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;

for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4);
mutablePos.setY(blockY);
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
final int blockZ = currZ | (currChunkZ << 4);
mutablePos.setZ(blockZ);
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
final int blockX = currX | (currChunkX << 4);
mutablePos.setX(blockX);

final BlockState blockState = blocks.get(localBlockIndex);
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));

if (blockState.is(Blocks.LAVA) || blockState.is(BlockTags.FIRE)) {
return new NoneMatchStream<>(false);
Expand Down Expand Up @@ -426,9 +412,7 @@ public void checkInsideBlocks() {

for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);

final LevelChunkSection[] sections = chunk.getSections();
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();

for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
Expand All @@ -451,17 +435,13 @@ public void checkInsideBlocks() {
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;

for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
final int blockY = currY | (currChunkY << 4);
mutablePos.setY(blockY);
mutablePos.setY(currY | (currChunkY << 4));
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
final int blockZ = currZ | (currChunkZ << 4);
mutablePos.setZ(blockZ);
mutablePos.setZ(currZ | (currChunkZ << 4));
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
final int blockX = currX | (currChunkX << 4);
mutablePos.setX(blockX);
mutablePos.setX(currX | (currChunkX << 4));

final BlockState blockState = blocks.get(localBlockIndex);
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));

if (!this.isAlive()) {
return;
Expand All @@ -476,23 +456,4 @@ public void checkInsideBlocks() {
}
}
}

/**
* @reason Optimise implementation
* @author Spottedleaf
*/
@Overwrite
public boolean touchingUnloadedChunk() {
final AABB box = this.getBoundingBox();

final int minBlockX = Mth.floor(box.minX) - 1;
final int minBlockZ = Mth.floor(box.minZ) - 1;
final int maxBlockX = Mth.ceil(box.maxX) + 1;
final int maxBlockZ = Mth.ceil(box.maxZ) + 1;

return !((ChunkSystemLevel)this.level).moonrise$areChunksLoaded(
minBlockX >> 4, minBlockZ >> 4,
maxBlockX >> 4, maxBlockZ >> 4
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ public Optional<BlockPos> findSupportingBlock(final Entity entity, final AABB aa

VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();

if ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
if (blockCollision == null) {
blockCollision = blockData.getCollisionShape((Level)(Object)this, mutablePos, collisionShape);

Expand Down

0 comments on commit a4770ac

Please sign in to comment.