Leaf: Lithium faster hash palette
This is a part of Leaf(https://github.com/Winds-Studio/Leaf/blob/8293ae2d0ada9df24e944f755b50e93208c1496d/leaf-server/minecraft-patches/features/0281-Lithium-faster-hash-palette.patch) Original proj license: https://github.com/Winds-Studio/Leaf/blob/8293ae2d0ada9df24e944f755b50e93208c1496d/LICENSE.md This patch is based on the following mixins: * "net/caffeinemc/mods/lithium/mixin/chunk/palette/StrategyMixin.java" By: 2No2Name <2No2Name@web.de> As part of: Lithium (https://github.com/CaffeineMC/lithium) Licensed under: LGPL-3.0-only (https://www.gnu.org/licenses/lgpl-3.0.html)
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: NanaChiyo0721 <nanachiyo0721@163.com>
|
||||
Date: Mon, 20 Jul 2026 20:41:53 +0800
|
||||
Subject: [PATCH] Leaf: Lithium: faster hash palette
|
||||
|
||||
This is a part of Leaf(https://github.com/Winds-Studio/Leaf/blob/8293ae2d0ada9df24e944f755b50e93208c1496d/leaf-server/minecraft-patches/features/0281-Lithium-faster-hash-palette.patch)
|
||||
|
||||
Original proj license: https://github.com/Winds-Studio/Leaf/blob/8293ae2d0ada9df24e944f755b50e93208c1496d/LICENSE.md
|
||||
|
||||
This patch is based on the following mixins:
|
||||
* "net/caffeinemc/mods/lithium/mixin/chunk/palette/StrategyMixin.java"
|
||||
By: 2No2Name <2No2Name@web.de>
|
||||
As part of: Lithium (https://github.com/CaffeineMC/lithium)
|
||||
Licensed under: LGPL-3.0-only (https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/HashMapPalette.java b/net/minecraft/world/level/chunk/HashMapPalette.java
|
||||
index 3ee35885ea1f997bb5a36dd8d5f83b168883ba61..2cee753e9927f04d8c77e179f34442ef8469de76 100644
|
||||
--- a/net/minecraft/world/level/chunk/HashMapPalette.java
|
||||
+++ b/net/minecraft/world/level/chunk/HashMapPalette.java
|
||||
@@ -14,7 +14,7 @@ public class HashMapPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.pa
|
||||
|
||||
// Paper start - optimise palette reads
|
||||
@Override
|
||||
- public final T[] moonrise$getRawPalette(final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> container) {
|
||||
+ public T[] moonrise$getRawPalette(final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> container) { // Leaf - Lithium - faster hash palette - public final -> public
|
||||
return ((ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T>)this.values).moonrise$getRawPalette(container);
|
||||
}
|
||||
// Paper end - optimise palette reads
|
||||
@@ -28,6 +28,14 @@ public class HashMapPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.pa
|
||||
this(bits, CrudeIncrementalIntIdentityHashBiMap.create((1 << bits) + 1)); // Paper - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap
|
||||
}
|
||||
|
||||
+ // Leaf start - Lithium - faster hash palette
|
||||
+ protected HashMapPalette(int bits, boolean dummy) {
|
||||
+ this.bits = bits;
|
||||
+ //noinspection DataFlowIssue
|
||||
+ this.values = dummy ? null : CrudeIncrementalIntIdentityHashBiMap.create((1 << bits) + 1);
|
||||
+ }
|
||||
+ // Leaf end - Lithium - faster hash palette
|
||||
+
|
||||
private HashMapPalette(final int bits, final CrudeIncrementalIntIdentityHashBiMap<T> values) {
|
||||
this.bits = bits;
|
||||
this.values = values;
|
||||
diff --git a/net/minecraft/world/level/chunk/Strategy.java b/net/minecraft/world/level/chunk/Strategy.java
|
||||
index 958be147cd940ac430576ca5b4ada70fa5d051e3..d88f93a581c7087540872e8ad0e82d1d3f676db2 100644
|
||||
--- a/net/minecraft/world/level/chunk/Strategy.java
|
||||
+++ b/net/minecraft/world/level/chunk/Strategy.java
|
||||
@@ -4,6 +4,15 @@ import net.minecraft.core.IdMap;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public abstract class Strategy<T> {
|
||||
+ // Leaf start - Lithium - faster hash palette
|
||||
+ private static final Palette.Factory HASH = net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette::create;
|
||||
+ //private static final Configuration HASH_3BIT = new Configuration.Simple(HASH, 3);
|
||||
+ private static final Configuration HASH_4BIT = new Configuration.Simple(HASH, 4);
|
||||
+ private static final Configuration HASH_5BIT = new Configuration.Simple(HASH, 5);
|
||||
+ private static final Configuration HASH_6BIT = new Configuration.Simple(HASH, 6);
|
||||
+ private static final Configuration HASH_7BIT = new Configuration.Simple(HASH, 7);
|
||||
+ private static final Configuration HASH_8BIT = new Configuration.Simple(HASH, 8);
|
||||
+ // Leaf end - Lithium - faster hash palette
|
||||
public static final Palette.Factory SINGLE_VALUE_PALETTE_FACTORY = SingleValuePalette::create; // Paper - Anti-Xray
|
||||
private static final Palette.Factory LINEAR_PALETTE_FACTORY = LinearPalette::create;
|
||||
private static final Palette.Factory HASHMAP_PALETTE_FACTORY = HashMapPalette::create;
|
||||
@@ -36,11 +45,14 @@ public abstract class Strategy<T> {
|
||||
public Configuration getConfigurationForBitCount(final int entryBits) {
|
||||
return switch (entryBits) {
|
||||
case 0 -> Strategy.ZERO_BITS;
|
||||
- case 1, 2, 3, 4 -> Strategy.FOUR_BITS_LINEAR;
|
||||
- case 5 -> Strategy.FIVE_BITS_HASHMAP;
|
||||
- case 6 -> Strategy.SIX_BITS_HASHMAP;
|
||||
- case 7 -> Strategy.SEVEN_BITS_HASHMAP;
|
||||
- case 8 -> Strategy.EIGHT_BITS_HASHMAP;
|
||||
+ // Leaf start - Lithium - faster hash palette
|
||||
+ case 1, 2 -> Strategy.FOUR_BITS_LINEAR;
|
||||
+ case 3, 4 -> Strategy.HASH_4BIT;
|
||||
+ case 5 -> Strategy.HASH_5BIT;
|
||||
+ case 6 -> Strategy.HASH_6BIT;
|
||||
+ case 7 -> Strategy.HASH_7BIT;
|
||||
+ case 8 -> Strategy.HASH_8BIT;
|
||||
+ // Leaf end - Lithium - faster hash palette
|
||||
default -> new Configuration.Global(this.globalPaletteBitsInMemory, entryBits);
|
||||
};
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
// Leaf - Lithium - faster hash palette
|
||||
|
||||
package net.caffeinemc.mods.lithium.common.world.chunk;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData;
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.VarInt;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.MissingPaletteEntryException;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import net.minecraft.world.level.chunk.PaletteResize;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR;
|
||||
|
||||
/**
|
||||
* Generally provides better performance over the vanilla {@link net.minecraft.world.level.chunk.HashMapPalette} when calling
|
||||
* {@link LithiumHashPalette#idFor(Object, PaletteResize)} through using a faster backing map and reducing pointer chasing.
|
||||
*/
|
||||
@NullMarked
|
||||
public final class LithiumHashPalette<T> extends HashMapPalette<T> implements Palette<T>, FastPalette<T> {
|
||||
private static final int ABSENT_VALUE = -1;
|
||||
|
||||
private final int indexBits;
|
||||
|
||||
private final Reference2IntOpenHashMap<T> table;
|
||||
private T[] entries;
|
||||
private int size = 0;
|
||||
|
||||
private LithiumHashPalette(int indexBits, T[] entries, Reference2IntOpenHashMap<T> table, int size) {
|
||||
super(size, true);
|
||||
this.indexBits = indexBits;
|
||||
this.entries = entries;
|
||||
this.table = table;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public LithiumHashPalette(int bits, List<T> list) {
|
||||
this(bits);
|
||||
|
||||
for (T t : list) {
|
||||
this.addEntry(t);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public LithiumHashPalette(int bits) {
|
||||
super(bits, true);
|
||||
this.indexBits = bits;
|
||||
|
||||
int capacity = 1 << bits;
|
||||
|
||||
this.entries = (T[]) new Object[capacity];
|
||||
this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR);
|
||||
this.table.defaultReturnValue(ABSENT_VALUE);
|
||||
}
|
||||
|
||||
// Leaf start - Sync moonrise changes
|
||||
@Override
|
||||
public T[] moonrise$getRawPalette(final FastPaletteData<T> container) {
|
||||
return this.entries;
|
||||
}
|
||||
// Leaf end - Sync moonrise changes
|
||||
|
||||
@Override
|
||||
public int idFor(T obj, PaletteResize<T> paletteResize) {
|
||||
int id = this.table.getInt(obj);
|
||||
|
||||
if (id == ABSENT_VALUE) {
|
||||
id = this.computeEntry(obj, paletteResize);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean maybeHas(Predicate<T> predicate) {
|
||||
for (int i = 0; i < this.size; ++i) {
|
||||
if (predicate.test(this.entries[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int computeEntry(T obj, PaletteResize<T> paletteResize) {
|
||||
int id = this.addEntry(obj);
|
||||
|
||||
if (id >= 1 << this.indexBits) {
|
||||
if (paletteResize == null) {
|
||||
throw new IllegalStateException("Cannot grow");
|
||||
} else {
|
||||
id = paletteResize.onResize(this.indexBits + 1, obj);
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private int addEntry(T obj) {
|
||||
int nextId = this.size;
|
||||
|
||||
if (nextId >= this.entries.length) {
|
||||
this.resize(this.size);
|
||||
}
|
||||
|
||||
this.table.put(obj, nextId);
|
||||
this.entries[nextId] = obj;
|
||||
|
||||
this.size++;
|
||||
|
||||
return nextId;
|
||||
}
|
||||
|
||||
private void resize(int neededCapacity) {
|
||||
this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T valueFor(int id) {
|
||||
T[] entries = this.entries;
|
||||
|
||||
T entry = null;
|
||||
if (id >= 0 && id < entries.length) {
|
||||
entry = entries[id];
|
||||
}
|
||||
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
} else {
|
||||
throw this.missingPaletteEntryCrash(id);
|
||||
}
|
||||
}
|
||||
|
||||
private ReportedException missingPaletteEntryCrash(int id) {
|
||||
try {
|
||||
throw new MissingPaletteEntryException(id);
|
||||
} catch (MissingPaletteEntryException e) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(e, "[Lithium] Getting Palette Entry");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk section");
|
||||
crashReportCategory.setDetail("IndexBits", this.indexBits);
|
||||
crashReportCategory.setDetail("Entries", this.entries.length + " Elements: " + Arrays.toString(this.entries));
|
||||
crashReportCategory.setDetail("Table", this.table.size() + " Elements: " + this.table);
|
||||
return new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(FriendlyByteBuf buf, IdMap<T> idMap) {
|
||||
this.clear();
|
||||
|
||||
int entryCount = buf.readVarInt();
|
||||
|
||||
for (int i = 0; i < entryCount; ++i) {
|
||||
this.addEntry(idMap.byIdOrThrow(buf.readVarInt()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf, IdMap<T> idMap) {
|
||||
int size = this.size;
|
||||
buf.writeVarInt(size);
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
buf.writeVarInt(idMap.getId(this.valueFor(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSerializedSize(IdMap<T> idMap) {
|
||||
int size = VarInt.getByteSize(this.size);
|
||||
|
||||
for (int i = 0; i < this.size; ++i) {
|
||||
size += VarInt.getByteSize(idMap.getId(this.valueFor(i)));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Palette<T> copy() {
|
||||
return new LithiumHashPalette<>(this.indexBits, this.entries.clone(), this.table.clone(), this.size);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
Arrays.fill(this.entries, null);
|
||||
this.table.clear();
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
public List<T> getElements() {
|
||||
T[] copy = Arrays.copyOf(this.entries, this.size);
|
||||
return Arrays.asList(copy);
|
||||
}
|
||||
|
||||
// Leaf start - override getEntries
|
||||
@Override
|
||||
public List<T> getEntries() {
|
||||
T[] copy = Arrays.copyOf(this.entries, this.size);
|
||||
return Arrays.asList(copy);
|
||||
}
|
||||
// Leaf end - override getEntries
|
||||
|
||||
public static <A> Palette<A> create(int bits, List<A> list) {
|
||||
return new LithiumHashPalette<>(bits, list);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user