Optimise palette
Palette has 4 implementations: global, hash, linear, and single. When only one implementation is being used (i.e all chunks loaded have sections with linear palette) the JIT can optimise the call to #valueFor quite well. When there are two implementations (i.e hash, linear) the JIT can still optimise the call but at a slight cost. However, when there are three or more in use (i.e linear, hash, single/global) the JIT cannot optimise the call and must revert to an interface invoke - which is terribly slow. In order to optimise this, we can extract an array for the hash, linear, and single palette implementations and perform lookups on the array directly instead of invoking
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fast_palette;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalettedContainer;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.util.CrudeIncrementalIntIdentityHashBiMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(CrudeIncrementalIntIdentityHashBiMap.class)
|
||||
public abstract class CrudeIncrementalIntIdentityHashBiMapMixin<K> implements IdMap<K>, FastPalette<K> {
|
||||
|
||||
@Shadow
|
||||
private K[] byId;
|
||||
|
||||
|
||||
@Unique
|
||||
private FastPalettedContainer<K> reference;
|
||||
|
||||
@Override
|
||||
public K[] moonrise$getRawPalette(final FastPalettedContainer<K> src) {
|
||||
this.reference = src;
|
||||
return this.byId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to call back to paletted container to update raw palette array
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "grow",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void growHook(final CallbackInfo ci) {
|
||||
final FastPalettedContainer<K> ref = this.reference;
|
||||
if (ref != null) {
|
||||
ref.moonrise$updatePaletteArray(this.byId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fast_palette;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalettedContainer;
|
||||
import net.minecraft.util.CrudeIncrementalIntIdentityHashBiMap;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(HashMapPalette.class)
|
||||
public abstract class HashMapPaletteMixin<T> implements Palette<T>, FastPalette<T> {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private CrudeIncrementalIntIdentityHashBiMap<T> values;
|
||||
|
||||
@Override
|
||||
public T[] moonrise$getRawPalette(final FastPalettedContainer<T> container) {
|
||||
return ((FastPalette<T>)this.values).moonrise$getRawPalette(container);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fast_palette;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalettedContainer;
|
||||
import net.minecraft.world.level.chunk.LinearPalette;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(LinearPalette.class)
|
||||
public abstract class LinearPaletteMixin<T> implements Palette<T>, FastPalette<T> {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private T[] values;
|
||||
|
||||
@Override
|
||||
public T[] moonrise$getRawPalette(final FastPalettedContainer<T> container) {
|
||||
return this.values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fast_palette;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(Palette.class)
|
||||
public interface PaletteMixin<T> extends FastPalette<T> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fast_palette;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalettedContainer;
|
||||
import net.minecraft.world.level.chunk.PaletteResize;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.PalettedContainerRO;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(PalettedContainer.class)
|
||||
public abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedContainerRO<T>, FastPalettedContainer<T> {
|
||||
|
||||
@Shadow
|
||||
public volatile PalettedContainer.Data<T> data;
|
||||
|
||||
@Unique
|
||||
private T[] rawPalette;
|
||||
|
||||
@Unique
|
||||
private void updateData(final PalettedContainer.Data<T> data) {
|
||||
if (data != null) {
|
||||
this.rawPalette = ((FastPalette<T>)data.palette).moonrise$getRawPalette(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$updatePaletteArray(final T[] palette) {
|
||||
this.rawPalette = palette;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Ljava/lang/Object;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook1(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Ljava/util/List;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook2(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on object construction
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/core/IdMap;Lnet/minecraft/world/level/chunk/PalettedContainer$Strategy;Lnet/minecraft/world/level/chunk/PalettedContainer$Data;)V",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void constructorHook3(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on palette resize
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "onResize",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void resizeHook(final CallbackInfoReturnable<Integer> cir) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Hook to update raw palette data on clientside read
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Inject(
|
||||
method = "read",
|
||||
at = @At(
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private void readHook(final CallbackInfo ci) {
|
||||
this.updateData(this.data);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private T readPaletteSlow(final int paletteIdx) {
|
||||
return this.data.palette.valueFor(paletteIdx);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private T readPalette(final int paletteIdx) {
|
||||
final T[] palette = this.rawPalette;
|
||||
if (palette == null) {
|
||||
return this.readPaletteSlow(paletteIdx);
|
||||
}
|
||||
|
||||
final T ret = palette[paletteIdx];
|
||||
if (ret == null) {
|
||||
throw new IllegalArgumentException("Palette index out of bounds");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Replace palette read with optimised version
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
private T getAndSet(final int index, final T value) {
|
||||
final int paletteIdx = this.data.palette.idFor(value);
|
||||
final int prev = this.data.storage.getAndSet(index, paletteIdx);
|
||||
return this.readPalette(prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @reason Replace palette read with optimised version
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Overwrite
|
||||
public T get(final int index) {
|
||||
return this.readPalette(this.data.storage.get(index));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ca.spottedleaf.moonrise.mixin.fast_palette;
|
||||
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalette;
|
||||
import ca.spottedleaf.moonrise.patches.fast_palette.FastPalettedContainer;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(SingleValuePalette.class)
|
||||
public abstract class SingleValuePaletteMixin<T> implements Palette<T>, FastPalette<T> {
|
||||
|
||||
@Shadow
|
||||
private T value;
|
||||
|
||||
@Override
|
||||
public T[] moonrise$getRawPalette(final FastPalettedContainer<T> container) {
|
||||
return (T[])new Object[] { this.value };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ca.spottedleaf.moonrise.patches.fast_palette;
|
||||
|
||||
public interface FastPalette<T> {
|
||||
|
||||
public default T[] moonrise$getRawPalette(final FastPalettedContainer<T> src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ca.spottedleaf.moonrise.patches.fast_palette;
|
||||
|
||||
public interface FastPalettedContainer<T> {
|
||||
|
||||
public void moonrise$updatePaletteArray(final T[] palette);
|
||||
|
||||
}
|
||||
@@ -39,6 +39,12 @@
|
||||
"explosions.ExplosionMixin",
|
||||
"explosions.ExplosionProfileMixin",
|
||||
"farm_block.FarmBlockMixin",
|
||||
"fast_palette.CrudeIncrementalIntIdentityHashBiMapMixin",
|
||||
"fast_palette.HashMapPaletteMixin",
|
||||
"fast_palette.LinearPaletteMixin",
|
||||
"fast_palette.PalettedContainerMixin",
|
||||
"fast_palette.PaletteMixin",
|
||||
"fast_palette.SingleValuePaletteMixin",
|
||||
"fluid.FlowingFluidMixin",
|
||||
"fluid.FluidStateMixin",
|
||||
"keep_alive_client.ServerGamePacketListenerImplMixin",
|
||||
|
||||
Reference in New Issue
Block a user