improve blockstate lookups

This commit is contained in:
Taiyou06
2024-07-06 21:34:19 +03:00
parent 3d69cdcb52
commit 15703cbcef
4 changed files with 181 additions and 1 deletions

View File

@@ -0,0 +1,74 @@
// Nitori Copyright (C) 2024 Gensokyo Reimagined
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package net.gensokyoreimagined.nitori.common.util.collections;
import it.unimi.dsi.fastutil.HashCommon;
import net.minecraft.util.Mth;
import java.util.function.Predicate;
/**
* A lossy hashtable implementation that stores a mapping between an object and a boolean.
* <p>
* Any hash collisions will result in an overwrite: this is safe because the correct value can always be recomputed,
* given that the given operator is deterministic.
* <p>
* This implementation is safe to use from multiple threads
*/
public final class Object2BooleanCacheTable<T> {
private final int mask;
private final Node<T>[] nodes;
private final Predicate<T> operator;
@SuppressWarnings("unchecked")
public Object2BooleanCacheTable(int capacity, Predicate<T> operator) {
int capacity1 = Mth.smallestEncompassingPowerOfTwo(capacity);
this.mask = capacity1 - 1;
this.nodes = (Node<T>[]) new Node[capacity1];
this.operator = operator;
}
private static <T> int hash(T key) {
return HashCommon.mix(key.hashCode());
}
public boolean get(T key) {
int idx = hash(key) & this.mask;
Node<T> node = this.nodes[idx];
if (node != null && key.equals(node.key)) {
return node.value;
}
boolean test = this.operator.test(key);
this.nodes[idx] = new Node<>(key, test);
return test;
}
static class Node<T> {
final T key;
final boolean value;
Node(T key, boolean value) {
this.key = key;
this.value = value;
}
}
}

View File

@@ -0,0 +1,64 @@
// Nitori Copyright (C) 2024 Gensokyo Reimagined
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package net.gensokyoreimagined.nitori.mixin.cached_hashcode;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Block.BlockStatePairKey.class)
public class BlockNeighborGroupMixin {
@Shadow
@Final
private BlockState first;
@Shadow
@Final
private BlockState second;
@Shadow
@Final
private Direction direction;
private int hash;
/**
* @reason Initialize the object's hashcode and cache it
*/
@Inject(method = "<init>", at = @At("RETURN"))
private void generateHash(BlockState blockState_1, BlockState blockState_2, Direction direction_1, CallbackInfo ci) {
int hash = this.first.hashCode();
hash = 31 * hash + this.second.hashCode();
hash = 31 * hash + this.direction.hashCode();
this.hash = hash;
}
/**
* @reason Uses the cached hashcode
* @author JellySquid
*/
@Overwrite(remap = false)
public int hashCode() {
return this.hash;
}
}

View File

@@ -0,0 +1,40 @@
// Nitori Copyright (C) 2024 Gensokyo Reimagined
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package net.gensokyoreimagined.nitori.mixin.shapes.blockstate_cache;
import net.gensokyoreimagined.nitori.common.util.collections.Object2BooleanCacheTable;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.level.block.Block;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@Mixin(Block.class)
public class BlockMixin {
private static final Object2BooleanCacheTable<VoxelShape> FULL_CUBE_CACHE = new Object2BooleanCacheTable<>(
512,
shape -> !Shapes.joinIsNotEmpty(Shapes.block(), shape, BooleanOp.NOT_SAME)
);
/**
* @reason Use a faster cache implementation
* @author gegy1000
*/
@Overwrite
public static boolean isShapeFullBlock(VoxelShape shape) {
return FULL_CUBE_CACHE.get(shape);
}
}

View File

@@ -32,6 +32,8 @@
"MixinWorldGenRegion",
"alloc.ServerChunkManagerMixin",
"alloc.StateMixin",
"util.MixinLevelBlockEntityRetrieval"
"util.MixinLevelBlockEntityRetrieval",
"cached_hashcode.BlockNeighborGroupMixin",
"shapes.blockstate_cache.BlockMixin"
]
}