From f2aae3ef0fe952ee60a89e9bea44444c9be9a473 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Tue, 8 Sep 2020 17:56:02 +0200 Subject: [PATCH] Added a global Voxelshape cache for cable bus voxel shapes, since combining voxel shapes turns out to be an extremely expensive process (i.e. 1.7s for 10000x5 shapes). --- .../java/appeng/parts/CableBusContainer.java | 8 +- .../java/appeng/parts/VoxelShapeCache.java | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 src/main/java/appeng/parts/VoxelShapeCache.java diff --git a/src/main/java/appeng/parts/CableBusContainer.java b/src/main/java/appeng/parts/CableBusContainer.java index b2ddc03fb..a48f602c9 100644 --- a/src/main/java/appeng/parts/CableBusContainer.java +++ b/src/main/java/appeng/parts/CableBusContainer.java @@ -41,7 +41,6 @@ import net.minecraft.util.Hand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.shapes.VoxelShape; -import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.IBlockReader; import net.minecraft.world.World; @@ -82,7 +81,6 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I private final EnumSet myLayerFlags = EnumSet.noneOf(LayerFlags.class); private YesNo hasRedstone = YesNo.UNDECIDED; private IPartHost tcb; - // TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not. private boolean requiresDynamicRender = false; private boolean inWorld = false; // Cached collision shape for living entities @@ -1096,11 +1094,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I } } - VoxelShape shape = VoxelShapes.empty(); - for (final AxisAlignedBB bx : boxes) { - shape = VoxelShapes.or(shape, VoxelShapes.create(bx)); - } - return shape; + return VoxelShapeCache.get(boxes); } private void invalidateShapes() { diff --git a/src/main/java/appeng/parts/VoxelShapeCache.java b/src/main/java/appeng/parts/VoxelShapeCache.java new file mode 100644 index 000000000..52b28f3ac --- /dev/null +++ b/src/main/java/appeng/parts/VoxelShapeCache.java @@ -0,0 +1,81 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.parts; + +import java.util.List; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.shapes.IBooleanFunction; +import net.minecraft.util.math.shapes.VoxelShape; +import net.minecraft.util.math.shapes.VoxelShapes; + +/** + * While creation of a {@link VoxelShape} with + * {@link VoxelShapes#create(AxisAlignedBB)} is fast enough, combining voxel + * shapes with {@link VoxelShapes#or(VoxelShape, VoxelShape)} or any other + * combination method, as well as {@link VoxelShape#simplify()} are extremely + * slow. For example: Creating a VoxelShape for a list of 5 bounding boxes + * 10,000 times takes about 1.7 seconds. + * + *

+ * To reduce the impact of this on cables, we introduce a global voxel shape + * cache so that cables can share their combined voxel shapes better. + */ +final class VoxelShapeCache { + + // Why using a List here should not make much of a difference vs. using a Set: + // The part's bounding box depends on the side it is attached to, and the sides + // are iterated over in a fixed order, meaning the order of bounding boxes + // should + // be the same for a same set of parts. + private static final LoadingCache, VoxelShape> CACHE = CacheBuilder.newBuilder()// + .maximumSize(10000L)// + .build(new CacheLoader, VoxelShape>() { + @Override + public VoxelShape load(List key) { + return create(key); + } + }); + + private VoxelShapeCache() { + } + + public static VoxelShape get(List boxes) { + return CACHE.getUnchecked(boxes); + } + + private static VoxelShape create(List boxes) { + if (boxes.isEmpty()) { + return VoxelShapes.empty(); + } + + int i = 0; + VoxelShape shape = VoxelShapes.create(boxes.get(i)); + for (; i < boxes.size(); i++) { + AxisAlignedBB box = boxes.get(i); + shape = VoxelShapes.combine(shape, VoxelShapes.create(box), IBooleanFunction.OR); + } + return shape.simplify(); + } + +}