-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
5 changed files
with
283 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
src/main/java/mega/blendtronic/mixin/mixins/common/intcache/IntCacheMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* Blendtronic | ||
* | ||
* Copyright (C) 2021-2024 SirFell, the MEGA team | ||
* All Rights Reserved | ||
* | ||
* The above copyright notice and this permission notice shall be included | ||
* in all copies or substantial portions of the Software. | ||
* | ||
* This program 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. | ||
* | ||
* 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 Lesser General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package mega.blendtronic.mixin.mixins.common.intcache; | ||
|
||
import mega.blendtronic.modules.intcache.NewIntCache; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.Overwrite; | ||
|
||
import net.minecraft.world.gen.layer.IntCache; | ||
|
||
@Mixin(IntCache.class) | ||
public abstract class IntCacheMixin { | ||
/** | ||
* @author ah-OOG-ah | ||
* @reason The old methods are non-threadsafe and barely safe at all - they recycle all allocated instances instead | ||
* of explicitly releasing them. | ||
*/ | ||
@Overwrite | ||
public static synchronized int[] getIntCache(int size) { | ||
return NewIntCache.getCache(size); | ||
} | ||
} |
142 changes: 142 additions & 0 deletions
142
src/main/java/mega/blendtronic/mixin/mixins/common/intcache/WorldChunkManagerMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
* Blendtronic | ||
* | ||
* Copyright (C) 2021-2024 SirFell, the MEGA team | ||
* All Rights Reserved | ||
* | ||
* The above copyright notice and this permission notice shall be included | ||
* in all copies or substantial portions of the Software. | ||
* | ||
* This program 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. | ||
* | ||
* 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 Lesser General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package mega.blendtronic.mixin.mixins.common.intcache; | ||
|
||
import com.llamalad7.mixinextras.sugar.Local; | ||
import com.llamalad7.mixinextras.sugar.Share; | ||
import com.llamalad7.mixinextras.sugar.ref.LocalRef; | ||
import mega.blendtronic.modules.intcache.NewIntCache; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.Redirect; | ||
import org.spongepowered.asm.mixin.injection.Slice; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; | ||
|
||
import net.minecraft.world.ChunkPosition; | ||
import net.minecraft.world.biome.BiomeGenBase; | ||
import net.minecraft.world.biome.WorldChunkManager; | ||
|
||
import java.util.List; | ||
import java.util.Random; | ||
|
||
/** | ||
* @author ah-OOG-ah | ||
* @author falsepattern | ||
* | ||
* The hodgepodge version reimplements a lot of the logic with inject-cancels. This is more robust than their solution. | ||
*/ | ||
@Mixin(WorldChunkManager.class) | ||
public abstract class WorldChunkManagerMixin { | ||
@Redirect(method = { "getRainfall", "getBiomesForGeneration", "areBiomesViable", | ||
"getBiomeGenAt([Lnet/minecraft/world/biome/BiomeGenBase;IIIIZ)[Lnet/minecraft/world/biome/BiomeGenBase;", | ||
"findBiomePosition" }, | ||
at = @At(value = "INVOKE", | ||
target = "Lnet/minecraft/world/gen/layer/IntCache;resetIntCache()V")) | ||
private void nukeIntCacheReset() {} | ||
|
||
@Inject(method = "getRainfall", | ||
at = @At(value = "INVOKE_ASSIGN", | ||
target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", | ||
shift = At.Shift.AFTER)) | ||
private void getRainfall$cachePreReturn(float[] listToReuse, int x, int z, int width, int length, | ||
CallbackInfoReturnable<float[]> cir, @Local(name = "aint") int[] ints, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
intsPass.set(ints); | ||
} | ||
|
||
@Inject(method = "getRainfall", | ||
at = @At("RETURN")) | ||
private void getRainfall$releaseCache(float[] listToReuse, int x, int z, int width, int length, | ||
CallbackInfoReturnable<float[]> cir, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
NewIntCache.releaseCache(intsPass.get()); | ||
} | ||
|
||
@Inject(method = "getBiomesForGeneration", | ||
at = @At(value = "INVOKE_ASSIGN", | ||
target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", | ||
shift = At.Shift.AFTER)) | ||
private void getBiomesForGeneration$cachePreReturn(BiomeGenBase[] biomes, int x, int z, int width, int height, | ||
CallbackInfoReturnable<BiomeGenBase[]> cir, @Local(name = "aint") int[] ints, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
intsPass.set(ints); | ||
} | ||
|
||
@Inject(method = "getBiomesForGeneration", | ||
at = @At("RETURN")) | ||
private void getBiomesForGeneration$releaseCache(BiomeGenBase[] biomes, int x, int z, int width, int height, | ||
CallbackInfoReturnable<BiomeGenBase[]> cir, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
NewIntCache.releaseCache(intsPass.get()); | ||
} | ||
|
||
@Inject(method = "getBiomeGenAt([Lnet/minecraft/world/biome/BiomeGenBase;IIIIZ)[Lnet/minecraft/world/biome/BiomeGenBase;", | ||
at = @At(value = "INVOKE_ASSIGN", | ||
target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", | ||
shift = At.Shift.AFTER)) | ||
private void getBiomeGenAt$cachePreReturn(BiomeGenBase[] listToReuse, int x, int y, int width, int length, boolean cacheFlag, | ||
CallbackInfoReturnable<BiomeGenBase[]> cir, @Local(name = "aint") int[] ints, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
intsPass.set(ints); | ||
} | ||
|
||
@Inject(method = "getBiomeGenAt([Lnet/minecraft/world/biome/BiomeGenBase;IIIIZ)[Lnet/minecraft/world/biome/BiomeGenBase;", | ||
at = @At("RETURN"), | ||
slice = @Slice(from = @At(value = "INVOKE_ASSIGN", | ||
target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", | ||
shift = At.Shift.AFTER))) | ||
private void getBiomeGenAt$releaseCache(BiomeGenBase[] listToReuse, int x, int y, int width, int length, boolean cacheFlag, | ||
CallbackInfoReturnable<BiomeGenBase[]> cir, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
NewIntCache.releaseCache(intsPass.get()); | ||
} | ||
|
||
@Inject(method = "areBiomesViable", | ||
at = @At(value = "INVOKE_ASSIGN", | ||
target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", | ||
shift = At.Shift.AFTER)) | ||
private void areBiomesViable$cachePreReturn(int x, int z, int radius, List<BiomeGenBase> allowed, | ||
CallbackInfoReturnable<Boolean> cir, @Local(name = "aint") int[] ints, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
intsPass.set(ints); | ||
} | ||
|
||
@Inject(method = "areBiomesViable", | ||
at = @At("RETURN")) | ||
private void areBiomesViable$releaseCache(int x, int z, int radius, List<BiomeGenBase> allowed, | ||
CallbackInfoReturnable<Boolean> cir, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
NewIntCache.releaseCache(intsPass.get()); | ||
} | ||
|
||
@Inject(method = "findBiomePosition", | ||
at = @At(value = "INVOKE_ASSIGN", | ||
target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", | ||
shift = At.Shift.AFTER)) | ||
private void findBiomePosition$cachePreReturn(int p_150795_1_, int p_150795_2_, int p_150795_3_, List<BiomeGenBase> p_150795_4_, Random p_150795_5_, | ||
CallbackInfoReturnable<ChunkPosition> cir, @Local(name = "aint") int[] ints, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
intsPass.set(ints); | ||
} | ||
|
||
@Inject(method = "findBiomePosition", | ||
at = @At("RETURN")) | ||
private void findBiomePosition$releaseCache(int p_150795_1_, int p_150795_2_, int p_150795_3_, List<BiomeGenBase> p_150795_4_, Random p_150795_5_, | ||
CallbackInfoReturnable<ChunkPosition> cir, @Share("intsPass") LocalRef<int[]> intsPass) { | ||
NewIntCache.releaseCache(intsPass.get()); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
src/main/java/mega/blendtronic/modules/intcache/NewIntCache.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* Blendtronic | ||
* | ||
* Copyright (C) 2021-2024 SirFell, the MEGA team | ||
* All Rights Reserved | ||
* | ||
* The above copyright notice and this permission notice shall be included | ||
* in all copies or substantial portions of the Software. | ||
* | ||
* This program 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. | ||
* | ||
* 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 Lesser General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package mega.blendtronic.modules.intcache; | ||
|
||
import java.util.List; | ||
import java.util.concurrent.Semaphore; | ||
|
||
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction; | ||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; | ||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; | ||
import it.unimi.dsi.fastutil.objects.ObjectArrayList; | ||
import it.unimi.dsi.fastutil.objects.ObjectList; | ||
|
||
/** | ||
* Ported over from Hodgepodge, with some extra optimizations on our side | ||
* | ||
* @author ah-OOG-ah | ||
* @author falsepattern | ||
*/ | ||
public class NewIntCache { | ||
|
||
private static final int SMALLEST = 256; | ||
private static final int MIN_LEVEL = 32 - Integer.numberOfLeadingZeros(SMALLEST - 1); | ||
|
||
private static final Int2ObjectMap<ObjectList<int[]>> cachedObjects = new Int2ObjectOpenHashMap<>(); | ||
|
||
private static final Semaphore SEMAPHORE = new Semaphore(1); | ||
|
||
@SuppressWarnings("Convert2Lambda") //No lambdas to minimize allocations | ||
private static final Int2ObjectFunction<ObjectList<int[]>> LIST_CONSTRUCTOR = new Int2ObjectFunction<>() { | ||
@Override | ||
public ObjectList<int[]> get(int key) { | ||
return new ObjectArrayList<>(16); | ||
} | ||
}; | ||
|
||
public static int[] getCache(int size) { | ||
// Get the smallest power of two larger than or equal to the number | ||
final int level = (size <= SMALLEST) ? MIN_LEVEL : 32 - Integer.numberOfLeadingZeros(size - 1); | ||
|
||
while (!SEMAPHORE.tryAcquire()) { | ||
Thread.onSpinWait(); | ||
} | ||
try { | ||
final List<int[]> caches = cachedObjects.computeIfAbsent(level, LIST_CONSTRUCTOR); | ||
|
||
if (caches.isEmpty()) { | ||
return new int[2 << (level - 1)]; | ||
} | ||
return caches.removeLast(); | ||
} finally { | ||
SEMAPHORE.release(); | ||
} | ||
} | ||
|
||
public static void releaseCache(int[] cache) { | ||
final int level = (cache.length <= SMALLEST) ? MIN_LEVEL : 32 - Integer.numberOfLeadingZeros(cache.length - 1); | ||
while (!SEMAPHORE.tryAcquire()) { | ||
Thread.onSpinWait(); | ||
} | ||
try { | ||
cachedObjects.computeIfAbsent(level, LIST_CONSTRUCTOR).add(cache); | ||
} finally { | ||
SEMAPHORE.release(); | ||
} | ||
} | ||
} |