mirror of
https://github.com/BX-Team/DivineMC.git
synced 2025-12-19 14:59:25 +00:00
1198 lines
58 KiB
Diff
1198 lines
58 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
|
|
Date: Wed, 12 Feb 2025 01:05:50 +0300
|
|
Subject: [PATCH] C2ME: Density Function Compiler
|
|
|
|
This functionality compiles density functions from world generation
|
|
datapacks (including vanilla generation) to JVM bytecode to increase
|
|
performance by allowing JVM JIT to better optimize the code.
|
|
All functions provided by vanilla are implemented.
|
|
|
|
About Density function: https://minecraft.wiki/w/Density_function
|
|
|
|
diff --git a/net/minecraft/util/CubicSpline.java b/net/minecraft/util/CubicSpline.java
|
|
index c04229bbed9d1162ecec99b8042d1707e2fc09bc..45b01de060362d88a5f02a76d6e6dc01748a55f5 100644
|
|
--- a/net/minecraft/util/CubicSpline.java
|
|
+++ b/net/minecraft/util/CubicSpline.java
|
|
@@ -254,31 +254,47 @@ public interface CubicSpline<C, I extends BoundedFloatFunction<C>> extends Bound
|
|
|
|
@Override
|
|
public float apply(C input) {
|
|
- float f = this.coordinate.apply(input);
|
|
- int i = findIntervalStart(this.locations, f);
|
|
- int i1 = this.locations.length - 1;
|
|
- if (i < 0) {
|
|
- return linearExtend(f, this.locations, this.values.get(0).apply(input), this.derivatives, 0);
|
|
- } else if (i == i1) {
|
|
- return linearExtend(f, this.locations, this.values.get(i1).apply(input), this.derivatives, i1);
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ float point = this.coordinate.apply(input);
|
|
+ int rangeForLocation = findIntervalStart(this.locations, point);
|
|
+ int last = this.locations.length - 1;
|
|
+ if (rangeForLocation < 0) {
|
|
+ return linearExtend(point, this.locations, this.values.get(0).apply(input), this.derivatives, 0);
|
|
+ } else if (rangeForLocation == last) {
|
|
+ return linearExtend(point, this.locations, this.values.get(last).apply(input), this.derivatives, last);
|
|
} else {
|
|
- float f1 = this.locations[i];
|
|
- float f2 = this.locations[i + 1];
|
|
- float f3 = (f - f1) / (f2 - f1);
|
|
- BoundedFloatFunction<C> boundedFloatFunction = (BoundedFloatFunction<C>)this.values.get(i);
|
|
- BoundedFloatFunction<C> boundedFloatFunction1 = (BoundedFloatFunction<C>)this.values.get(i + 1);
|
|
- float f4 = this.derivatives[i];
|
|
- float f5 = this.derivatives[i + 1];
|
|
- float f6 = boundedFloatFunction.apply(input);
|
|
- float f7 = boundedFloatFunction1.apply(input);
|
|
- float f8 = f4 * (f2 - f1) - (f7 - f6);
|
|
- float f9 = -f5 * (f2 - f1) + (f7 - f6);
|
|
- return Mth.lerp(f3, f6, f7) + f3 * (1.0F - f3) * Mth.lerp(f3, f8, f9);
|
|
+ float loc0 = this.locations[rangeForLocation];
|
|
+ float loc1 = this.locations[rangeForLocation + 1];
|
|
+ float locDist = loc1 - loc0;
|
|
+ float k = (point - loc0) / locDist;
|
|
+ float n = this.values.get(rangeForLocation).apply(input);
|
|
+ float o = this.values.get(rangeForLocation + 1).apply(input);
|
|
+ float onDist = o - n;
|
|
+ float p = this.derivatives[rangeForLocation] * locDist - onDist;
|
|
+ float q = -this.derivatives[rangeForLocation + 1] * locDist + onDist;
|
|
+ return Mth.lerp(k, n, o) + k * (1.0F - k) * Mth.lerp(k, p, q);
|
|
}
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
private static int findIntervalStart(float[] locations, float start) {
|
|
- return Mth.binarySearch(0, locations.length, i -> start < locations[i]) - 1;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ int min = 0;
|
|
+ int i = locations.length;
|
|
+
|
|
+ while (i > 0) {
|
|
+ int j = i / 2;
|
|
+ int k = min + j;
|
|
+ if (start < locations[k]) {
|
|
+ i = j;
|
|
+ } else {
|
|
+ min = k + 1;
|
|
+ i -= j + 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return min - 1;
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
@VisibleForTesting
|
|
@@ -313,5 +329,27 @@ public interface CubicSpline<C, I extends BoundedFloatFunction<C>> extends Bound
|
|
this.derivatives
|
|
);
|
|
}
|
|
+
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ @Override
|
|
+ public boolean equals(Object o) {
|
|
+ if (this == o) return true;
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
+ Multipoint<?, ?> that = (Multipoint<?, ?>) o;
|
|
+ return java.util.Objects.equals(coordinate, that.coordinate()) && java.util.Arrays.equals(locations, that.locations()) && java.util.Objects.equals(values, that.values()) && java.util.Arrays.equals(derivatives, that.derivatives());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int hashCode() {
|
|
+ int result = 1;
|
|
+
|
|
+ result = 31 * result + java.util.Objects.hashCode(coordinate);
|
|
+ result = 31 * result + java.util.Arrays.hashCode(locations);
|
|
+ result = 31 * result + java.util.Objects.hashCode(values);
|
|
+ result = 31 * result + java.util.Arrays.hashCode(derivatives);
|
|
+
|
|
+ return result;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/world/level/levelgen/DensityFunctions.java b/net/minecraft/world/level/levelgen/DensityFunctions.java
|
|
index f2fd59359c69a379b9b0a359c7fc917890b3bb74..90531d659d8be9178d8e7ed9fc20a57d204cf07e 100644
|
|
--- a/net/minecraft/world/level/levelgen/DensityFunctions.java
|
|
+++ b/net/minecraft/world/level/levelgen/DensityFunctions.java
|
|
@@ -281,38 +281,66 @@ public final class DensityFunctions {
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
- this.argument1.fillArray(array, contextProvider);
|
|
- switch (this.type) {
|
|
- case ADD:
|
|
- double[] doubles = new double[array.length];
|
|
- this.argument2.fillArray(doubles, contextProvider);
|
|
-
|
|
- for (int i = 0; i < array.length; i++) {
|
|
- array[i] += doubles[i];
|
|
- }
|
|
- break;
|
|
- case MUL:
|
|
- for (int i1 = 0; i1 < array.length; i1++) {
|
|
- double d = array[i1];
|
|
- array[i1] = d == 0.0 ? 0.0 : d * this.argument2.compute(contextProvider.forIndex(i1));
|
|
- }
|
|
- break;
|
|
- case MIN:
|
|
- double d1 = this.argument2.minValue();
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ Runnable run = () -> {
|
|
+ this.argument1.fillArray(array, contextProvider);
|
|
+ switch (this.type) {
|
|
+ case ADD:
|
|
+ double[] doubles = new double[array.length];
|
|
+ this.argument2.fillArray(doubles, contextProvider);
|
|
+
|
|
+ for (int i = 0; i < array.length; i++) {
|
|
+ array[i] += doubles[i];
|
|
+ }
|
|
+ break;
|
|
+ case MUL:
|
|
+ for (int i1 = 0; i1 < array.length; i1++) {
|
|
+ double d = array[i1];
|
|
+ array[i1] = d == 0.0 ? 0.0 : d * this.argument2.compute(contextProvider.forIndex(i1));
|
|
+ }
|
|
+ break;
|
|
+ case MIN:
|
|
+ double d1 = this.argument2.minValue();
|
|
+
|
|
+ for (int i2 = 0; i2 < array.length; i2++) {
|
|
+ double d2 = array[i2];
|
|
+ array[i2] = d2 < d1 ? d2 : Math.min(d2, this.argument2.compute(contextProvider.forIndex(i2)));
|
|
+ }
|
|
+ break;
|
|
+ case MAX:
|
|
+ d1 = this.argument2.maxValue();
|
|
+
|
|
+ for (int i2 = 0; i2 < array.length; i2++) {
|
|
+ double d2 = array[i2];
|
|
+ array[i2] = d2 > d1 ? d2 : Math.max(d2, this.argument2.compute(contextProvider.forIndex(i2)));
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler && this.type == DensityFunctions.TwoArgumentSimpleFunction.Type.ADD) {
|
|
+ this.argument1.fillArray(array, contextProvider);
|
|
+ double[] ds;
|
|
|
|
- for (int i2 = 0; i2 < array.length; i2++) {
|
|
- double d2 = array[i2];
|
|
- array[i2] = d2 < d1 ? d2 : Math.min(d2, this.argument2.compute(contextProvider.forIndex(i2)));
|
|
- }
|
|
- break;
|
|
- case MAX:
|
|
- d1 = this.argument2.maxValue();
|
|
+ com.ishland.c2me.opts.dfc.common.util.ArrayCache arrayCache = contextProvider instanceof com.ishland.c2me.opts.dfc.common.ducks.IArrayCacheCapable arrayCacheCapable ? arrayCacheCapable.c2me$getArrayCache() : null;
|
|
|
|
- for (int i2 = 0; i2 < array.length; i2++) {
|
|
- double d2 = array[i2];
|
|
- array[i2] = d2 > d1 ? d2 : Math.max(d2, this.argument2.compute(contextProvider.forIndex(i2)));
|
|
- }
|
|
+ if (arrayCache != null) {
|
|
+ ds = arrayCache.getDoubleArray(array.length, false);
|
|
+ } else {
|
|
+ ds = new double[array.length];
|
|
+ }
|
|
+
|
|
+ this.argument2.fillArray(ds, contextProvider);
|
|
+
|
|
+ for (int i = 0; i < array.length; i++) {
|
|
+ array[i] += ds[i];
|
|
+ }
|
|
+
|
|
+ if (arrayCache != null) {
|
|
+ arrayCache.recycle(ds);
|
|
+ }
|
|
+ } else {
|
|
+ run.run();
|
|
}
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
@Override
|
|
@@ -756,7 +784,105 @@ public final class DensityFunctions {
|
|
}
|
|
}
|
|
|
|
- protected record Marker(@Override DensityFunctions.Marker.Type type, @Override DensityFunction wrapped) implements DensityFunctions.MarkerOrMarked {
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ public static final class Marker implements com.ishland.c2me.opts.dfc.common.ducks.IFastCacheLike, com.ishland.c2me.opts.dfc.common.ducks.IEqualityOverriding, MarkerOrMarked {
|
|
+ private final Type type;
|
|
+ private final DensityFunction wrapped;
|
|
+ private Object c2me$optionalEquality;
|
|
+
|
|
+ @Override
|
|
+ public boolean equals(final Object that) {
|
|
+ Function<Object, Boolean> original = (o) -> {
|
|
+ if (o == this) return true;
|
|
+ if (o == null || o.getClass() != this.getClass()) return false;
|
|
+ var a = (Marker) o;
|
|
+ return java.util.Objects.equals(this.type, a.type) &&
|
|
+ java.util.Objects.equals(this.wrapped, a.wrapped);
|
|
+ };
|
|
+ if (true) {
|
|
+ return original.apply(that);
|
|
+ }
|
|
+ Object a = this.c2me$getOverriddenEquality();
|
|
+ Object b = that instanceof com.ishland.c2me.opts.dfc.common.ducks.IEqualityOverriding equalityOverriding ? equalityOverriding.c2me$getOverriddenEquality() : null;
|
|
+ if (a == null) {
|
|
+ return original.apply(b != null ? b : that);
|
|
+ } else {
|
|
+ return a.equals(b != null ? b : that);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int hashCode() {
|
|
+ java.util.function.Supplier<Integer> original = () -> java.util.Objects.hash(type, wrapped);
|
|
+ Object c2me$optionalEquality1 = this.c2me$optionalEquality;
|
|
+ if (c2me$optionalEquality1 != null && false) {
|
|
+ return c2me$optionalEquality1.hashCode();
|
|
+ } else {
|
|
+ return original.get();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public Marker(Type type, DensityFunction wrapped) {
|
|
+ this.type = type;
|
|
+ this.wrapped = wrapped;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double c2me$getCached(int x, int y, int z, com.ishland.c2me.opts.dfc.common.ast.EvalType evalType) {
|
|
+ return Double.longBitsToDouble(CACHE_MISS_NAN_BITS);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean c2me$getCached(double[] res, int[] x, int[] y, int[] z, com.ishland.c2me.opts.dfc.common.ast.EvalType evalType) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(int x, int y, int z, com.ishland.c2me.opts.dfc.common.ast.EvalType evalType, double cached) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(double[] res, int[] x, int[] y, int[] z, com.ishland.c2me.opts.dfc.common.ast.EvalType evalType) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$getDelegate() {
|
|
+ return this.wrapped;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$withDelegate(DensityFunction delegate) {
|
|
+ DensityFunctions.Marker wrapping = new DensityFunctions.Marker(this.type(), delegate);
|
|
+ ((com.ishland.c2me.opts.dfc.common.ducks.IEqualityOverriding) (Object) wrapping).c2me$overrideEquality(this);
|
|
+ return wrapping;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$overrideEquality(Object object) {
|
|
+ Object inner = object;
|
|
+ while (true) {
|
|
+ Object inner1 = inner instanceof com.ishland.c2me.opts.dfc.common.ducks.IEqualityOverriding e1 ? e1.c2me$getOverriddenEquality() : null;
|
|
+ if (inner1 == null) {
|
|
+ this.c2me$optionalEquality = inner;
|
|
+ break;
|
|
+ }
|
|
+ inner = inner1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Object c2me$getOverriddenEquality() {
|
|
+ return this.c2me$optionalEquality;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction mapAll(Visitor visitor) {
|
|
+ return visitor.apply(this.c2me$withDelegate(this.wrapped().mapAll(visitor)));
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
+
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
return this.wrapped.compute(context);
|
|
@@ -777,7 +903,19 @@ public final class DensityFunctions {
|
|
return this.wrapped.maxValue();
|
|
}
|
|
|
|
- static enum Type implements StringRepresentable {
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ @Override
|
|
+ public Type type() {
|
|
+ return type;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction wrapped() {
|
|
+ return wrapped;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
+
|
|
+ public static enum Type implements StringRepresentable { // DivineMC - Density Function Compiler - make this public
|
|
Interpolated("interpolated"),
|
|
FlatCache("flat_cache"),
|
|
Cache2D("cache_2d"),
|
|
diff --git a/net/minecraft/world/level/levelgen/NoiseChunk.java b/net/minecraft/world/level/levelgen/NoiseChunk.java
|
|
index ff0c2aa0cdf7a88f67e6438dbd17d9c2bc39107a..3c3615043ab87da45b7ea47bf2f632a91a60dbc6 100644
|
|
--- a/net/minecraft/world/level/levelgen/NoiseChunk.java
|
|
+++ b/net/minecraft/world/level/levelgen/NoiseChunk.java
|
|
@@ -8,6 +8,7 @@ import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
+import java.util.function.Supplier;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.QuartPos;
|
|
import net.minecraft.core.SectionPos;
|
|
@@ -21,7 +22,18 @@ import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
import net.minecraft.world.level.levelgen.material.MaterialRuleList;
|
|
|
|
-public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunction.FunctionContext {
|
|
+// DivineMC start - Density Function Compiler
|
|
+import com.ishland.c2me.opts.dfc.common.ast.EvalType;
|
|
+import com.ishland.c2me.opts.dfc.common.ducks.IArrayCacheCapable;
|
|
+import com.ishland.c2me.opts.dfc.common.ducks.ICoordinatesFilling;
|
|
+import com.ishland.c2me.opts.dfc.common.ducks.IFastCacheLike;
|
|
+import com.ishland.c2me.opts.dfc.common.gen.DelegatingBlendingAwareVisitor;
|
|
+import com.ishland.c2me.opts.dfc.common.util.ArrayCache;
|
|
+import com.ishland.c2me.opts.dfc.common.vif.EachApplierVanillaInterface;
|
|
+import com.ishland.c2me.opts.dfc.common.vif.NoisePosVanillaInterface;
|
|
+// DivineMC end - Density Function Compiler
|
|
+
|
|
+public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunction.FunctionContext, IArrayCacheCapable, ICoordinatesFilling {
|
|
private final NoiseSettings noiseSettings;
|
|
final int cellCountXZ;
|
|
final int cellCountY;
|
|
@@ -57,7 +69,47 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
long interpolationCounter;
|
|
long arrayInterpolationCounter;
|
|
int arrayIndex;
|
|
- private final DensityFunction.ContextProvider sliceFillingContextProvider = new DensityFunction.ContextProvider() {
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ private final ArrayCache c2me$arrayCache = new ArrayCache();
|
|
+
|
|
+ @Override
|
|
+ public ArrayCache c2me$getArrayCache() {
|
|
+ return this.c2me$arrayCache != null ? this.c2me$arrayCache : new ArrayCache();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$fillCoordinates(int[] x, int[] y, int[] z) {
|
|
+ int index = 0;
|
|
+ for (int i = this.cellHeight - 1; i >= 0; i--) {
|
|
+ int blockY = this.cellStartBlockY + i;
|
|
+ for (int j = 0; j < this.cellWidth; j++) {
|
|
+ int blockX = this.cellStartBlockX + j;
|
|
+ for (int k = 0; k < this.cellWidth; k++) {
|
|
+ int blockZ = this.cellStartBlockZ + k;
|
|
+
|
|
+ x[index] = blockX;
|
|
+ y[index] = blockY;
|
|
+ z[index] = blockZ;
|
|
+
|
|
+ index++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private @org.jetbrains.annotations.NotNull DelegatingBlendingAwareVisitor c2me$getDelegatingBlendingAwareVisitor(DensityFunction.Visitor visitor) {
|
|
+ return new DelegatingBlendingAwareVisitor(visitor, this.getBlender() != Blender.empty());
|
|
+ }
|
|
+
|
|
+ private DensityFunction.Visitor modifyVisitor1(DensityFunction.Visitor visitor) {
|
|
+ return c2me$getDelegatingBlendingAwareVisitor(visitor);
|
|
+ }
|
|
+
|
|
+ private DensityFunction.Visitor modifyVisitor2(DensityFunction.Visitor visitor) {
|
|
+ return c2me$getDelegatingBlendingAwareVisitor(visitor);
|
|
+ }
|
|
+
|
|
+ public class NoiseChunkSliceFillingContextProvider implements DensityFunction.ContextProvider, IArrayCacheCapable, ICoordinatesFilling {
|
|
@Override
|
|
public DensityFunction.FunctionContext forIndex(int arrayIndex) {
|
|
NoiseChunk.this.cellStartBlockY = (arrayIndex + NoiseChunk.this.cellNoiseMinY) * NoiseChunk.this.cellHeight;
|
|
@@ -77,7 +129,23 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
values[i] = function.compute(NoiseChunk.this);
|
|
}
|
|
}
|
|
- };
|
|
+
|
|
+ @Override
|
|
+ public ArrayCache c2me$getArrayCache() {
|
|
+ return (NoiseChunk.this).c2me$getArrayCache();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$fillCoordinates(int[] x, int[] y, int[] z) {
|
|
+ for (int i = 0; i < (NoiseChunk.this).cellCountY + 1; i++) {
|
|
+ x[i] = (NoiseChunk.this).cellStartBlockX + (NoiseChunk.this).inCellX;
|
|
+ y[i] = (i + (NoiseChunk.this).cellNoiseMinY) * (NoiseChunk.this).cellHeight;
|
|
+ z[i] = (NoiseChunk.this).cellStartBlockZ + (NoiseChunk.this).inCellZ;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ private final DensityFunction.ContextProvider sliceFillingContextProvider = new NoiseChunkSliceFillingContextProvider();
|
|
+ // DivineMC end - Density Function Compiler
|
|
|
|
public static NoiseChunk forChunk(
|
|
ChunkAccess chunk,
|
|
@@ -140,7 +208,7 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
}
|
|
|
|
NoiseRouter noiseRouter = random.router();
|
|
- NoiseRouter noiseRouter1 = noiseRouter.mapAll(this::wrap);
|
|
+ NoiseRouter noiseRouter1 = noiseRouter.mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor1(this::wrap) : this::wrap); // DivineMC - Density Function Compiler
|
|
this.preliminarySurfaceLevel = noiseRouter1.preliminarySurfaceLevel();
|
|
if (!noiseGeneratorSettings.isAquifersEnabled()) {
|
|
this.aquifer = Aquifer.createDisabled(fluidPicker);
|
|
@@ -156,7 +224,7 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
DensityFunction densityFunction = DensityFunctions.cacheAllInCell(
|
|
DensityFunctions.add(noiseRouter1.finalDensity(), DensityFunctions.BeardifierMarker.INSTANCE)
|
|
)
|
|
- .mapAll(this::wrap);
|
|
+ .mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap); // DivineMC - Density Function Compiler
|
|
list.add(context -> this.aquifer.computeSubstance(context, densityFunction.compute(context)));
|
|
if (noiseGeneratorSettings.oreVeinsEnabled()) {
|
|
list.add(OreVeinifier.create(noiseRouter1.veinToggle(), noiseRouter1.veinRidged(), noiseRouter1.veinGap(), random.oreRandom()));
|
|
@@ -167,12 +235,14 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
|
|
protected Climate.Sampler cachedClimateSampler(NoiseRouter noiseRouter, List<Climate.ParameterPoint> points) {
|
|
return new Climate.Sampler(
|
|
- noiseRouter.temperature().mapAll(this::wrap),
|
|
- noiseRouter.vegetation().mapAll(this::wrap),
|
|
- noiseRouter.continents().mapAll(this::wrap),
|
|
- noiseRouter.erosion().mapAll(this::wrap),
|
|
- noiseRouter.depth().mapAll(this::wrap),
|
|
- noiseRouter.ridges().mapAll(this::wrap),
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ noiseRouter.temperature().mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap),
|
|
+ noiseRouter.vegetation().mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap),
|
|
+ noiseRouter.continents().mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap),
|
|
+ noiseRouter.erosion().mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap),
|
|
+ noiseRouter.depth().mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap),
|
|
+ noiseRouter.ridges().mapAll(org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler ? modifyVisitor2(this::wrap) : this::wrap),
|
|
+ // DivineMC end - Density Function Compiler
|
|
points
|
|
);
|
|
}
|
|
@@ -378,6 +448,13 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
}
|
|
|
|
private DensityFunction wrapNew(DensityFunction densityFunction) {
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler) {
|
|
+ if (this.interpolating && densityFunction instanceof DensityFunctions.Marker) {
|
|
+ throw new IllegalStateException("Cannot create more wrapping during interpolation loop");
|
|
+ }
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
if (densityFunction instanceof DensityFunctions.Marker marker) {
|
|
return (DensityFunction)(switch (marker.type()) {
|
|
case Interpolated -> new NoiseChunk.NoiseInterpolator(marker.wrapped());
|
|
@@ -487,10 +564,48 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
BlockState calculate(DensityFunction.FunctionContext context);
|
|
}
|
|
|
|
- static class Cache2D implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
+ static class Cache2D implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction, IFastCacheLike { // DivineMC - Density Function Compiler
|
|
private DensityFunction function;
|
|
private long lastPos2D = ChunkPos.INVALID_CHUNK_POS;
|
|
private double lastValue;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ @Override
|
|
+ public double c2me$getCached(int x, int y, int z, EvalType evalType) {
|
|
+ long l = ChunkPos.asLong(x, z);
|
|
+ if (this.lastPos2D == l) {
|
|
+ return this.lastValue;
|
|
+ } else {
|
|
+ return Double.longBitsToDouble(CACHE_MISS_NAN_BITS);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean c2me$getCached(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(int x, int y, int z, EvalType evalType, double cached) {
|
|
+ this.lastPos2D = ChunkPos.asLong(x, z);
|
|
+ this.lastValue = cached;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$getDelegate() {
|
|
+ return this.function;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$withDelegate(DensityFunction delegate) {
|
|
+ this.function = delegate;
|
|
+ return this;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
|
|
Cache2D(DensityFunction function) {
|
|
this.function = function;
|
|
@@ -527,9 +642,92 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
}
|
|
}
|
|
|
|
- class CacheAllInCell implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
- final DensityFunction noiseFiller;
|
|
+ class CacheAllInCell implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction, IFastCacheLike { // DivineMC - Density Function Compiler
|
|
+ DensityFunction noiseFiller; // DivineMC - remove final
|
|
final double[] values;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ @Override
|
|
+ public double c2me$getCached(int x, int y, int z, EvalType evalType) {
|
|
+ if (evalType == EvalType.INTERPOLATION) {
|
|
+ boolean isInInterpolationLoop = (NoiseChunk.this).interpolating;
|
|
+ if (isInInterpolationLoop) {
|
|
+ int startBlockX = (NoiseChunk.this).cellStartBlockX;
|
|
+ int startBlockY = (NoiseChunk.this).cellStartBlockY;
|
|
+ int startBlockZ = (NoiseChunk.this).cellStartBlockZ;
|
|
+ int horizontalCellBlockCount = (NoiseChunk.this).cellWidth;
|
|
+ int verticalCellBlockCount = (NoiseChunk.this).cellHeight;
|
|
+ int cellBlockX = x - startBlockX;
|
|
+ int cellBlockY = y - startBlockY;
|
|
+ int cellBlockZ = z - startBlockZ;
|
|
+ if (cellBlockX >= 0 &&
|
|
+ cellBlockY >= 0 &&
|
|
+ cellBlockZ >= 0 &&
|
|
+ cellBlockX < horizontalCellBlockCount &&
|
|
+ cellBlockY < verticalCellBlockCount &&
|
|
+ cellBlockZ < horizontalCellBlockCount) {
|
|
+ return this.values[((verticalCellBlockCount - 1 - cellBlockY) * horizontalCellBlockCount + cellBlockX)
|
|
+ * horizontalCellBlockCount
|
|
+ + cellBlockZ];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return CACHE_MISS_NAN_BITS;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean c2me$getCached(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ if (evalType == EvalType.INTERPOLATION) {
|
|
+ boolean isInInterpolationLoop = (NoiseChunk.this).interpolating;
|
|
+ if (isInInterpolationLoop) {
|
|
+ int startBlockX = (NoiseChunk.this).cellStartBlockX;
|
|
+ int startBlockY = (NoiseChunk.this).cellStartBlockY;
|
|
+ int startBlockZ = (NoiseChunk.this).cellStartBlockZ;
|
|
+ int horizontalCellBlockCount = (NoiseChunk.this).cellWidth;
|
|
+ int verticalCellBlockCount = (NoiseChunk.this).cellHeight;
|
|
+ for (int i = 0; i < res.length; i++) {
|
|
+ int cellBlockX = x[i] - startBlockX;
|
|
+ int cellBlockY = y[i] - startBlockY;
|
|
+ int cellBlockZ = z[i] - startBlockZ;
|
|
+ if (cellBlockX >= 0 &&
|
|
+ cellBlockY >= 0 &&
|
|
+ cellBlockZ >= 0 &&
|
|
+ cellBlockX < horizontalCellBlockCount &&
|
|
+ cellBlockY < verticalCellBlockCount &&
|
|
+ cellBlockZ < horizontalCellBlockCount) {
|
|
+ res[i] = this.values[((verticalCellBlockCount - 1 - cellBlockY) * horizontalCellBlockCount + cellBlockX) * horizontalCellBlockCount + cellBlockZ];
|
|
+ } else {
|
|
+ System.out.println("partial cell cache hit");
|
|
+ return false; // partial hit possible
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(int x, int y, int z, EvalType evalType, double cached) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$getDelegate() {
|
|
+ return this.noiseFiller;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$withDelegate(DensityFunction delegate) {
|
|
+ this.noiseFiller = delegate;
|
|
+ return this;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
|
|
CacheAllInCell(final DensityFunction noiseFilter) {
|
|
this.noiseFiller = noiseFilter;
|
|
@@ -539,18 +737,51 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
- if (context != NoiseChunk.this) {
|
|
- return this.noiseFiller.compute(context);
|
|
- } else if (!NoiseChunk.this.interpolating) {
|
|
- throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
- } else {
|
|
- int i = NoiseChunk.this.inCellX;
|
|
- int i1 = NoiseChunk.this.inCellY;
|
|
- int i2 = NoiseChunk.this.inCellZ;
|
|
- return i >= 0 && i1 >= 0 && i2 >= 0 && i < NoiseChunk.this.cellWidth && i1 < NoiseChunk.this.cellHeight && i2 < NoiseChunk.this.cellWidth
|
|
- ? this.values[((NoiseChunk.this.cellHeight - 1 - i1) * NoiseChunk.this.cellWidth + i) * NoiseChunk.this.cellWidth + i2]
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ Supplier<Double> run = () -> {
|
|
+ if (context != NoiseChunk.this) {
|
|
+ return this.noiseFiller.compute(context);
|
|
+ } else if (!NoiseChunk.this.interpolating) {
|
|
+ throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
+ } else {
|
|
+ int i = NoiseChunk.this.inCellX;
|
|
+ int i1 = NoiseChunk.this.inCellY;
|
|
+ int i2 = NoiseChunk.this.inCellZ;
|
|
+ return i >= 0 && i1 >= 0 && i2 >= 0 && i < NoiseChunk.this.cellWidth && i1 < NoiseChunk.this.cellHeight && i2 < NoiseChunk.this.cellWidth
|
|
+ ? this.values[((NoiseChunk.this.cellHeight - 1 - i1) * NoiseChunk.this.cellWidth + i) * NoiseChunk.this.cellWidth + i2]
|
|
+ : this.noiseFiller.compute(context);
|
|
+ }
|
|
+ };
|
|
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler || context instanceof NoiseChunk) {
|
|
+ return run.get();
|
|
+ }
|
|
+ if (context instanceof NoisePosVanillaInterface vif && vif.getType() == EvalType.INTERPOLATION) {
|
|
+ boolean isInInterpolationLoop = (NoiseChunk.this).interpolating;
|
|
+ if (!isInInterpolationLoop) {
|
|
+ return run.get();
|
|
+ }
|
|
+ int startBlockX = (NoiseChunk.this).cellStartBlockX;
|
|
+ int startBlockY = (NoiseChunk.this).cellStartBlockY;
|
|
+ int startBlockZ = (NoiseChunk.this).cellStartBlockZ;
|
|
+ int horizontalCellBlockCount = (NoiseChunk.this).cellWidth;
|
|
+ int verticalCellBlockCount = (NoiseChunk.this).cellHeight;
|
|
+ int cellBlockX = context.blockX() - startBlockX;
|
|
+ int cellBlockY = context.blockY() - startBlockY;
|
|
+ int cellBlockZ = context.blockZ() - startBlockZ;
|
|
+ return cellBlockX >= 0
|
|
+ && cellBlockY >= 0
|
|
+ && cellBlockZ >= 0
|
|
+ && cellBlockX < horizontalCellBlockCount
|
|
+ && cellBlockY < verticalCellBlockCount
|
|
+ && cellBlockZ < horizontalCellBlockCount
|
|
+ ? this.values[((verticalCellBlockCount - 1 - cellBlockY) * horizontalCellBlockCount + cellBlockX)
|
|
+ * horizontalCellBlockCount
|
|
+ + cellBlockZ]
|
|
: this.noiseFiller.compute(context);
|
|
}
|
|
+
|
|
+ return run.get();
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
@Override
|
|
@@ -569,13 +800,84 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
}
|
|
}
|
|
|
|
- class CacheOnce implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
+ class CacheOnce implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction, IFastCacheLike { // DivineMC - Density Function Compiler
|
|
private DensityFunction function;
|
|
private long lastCounter;
|
|
private long lastArrayCounter;
|
|
private double lastValue;
|
|
@Nullable
|
|
private double[] lastArray;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ private double c2me$lastValue = Double.NaN;
|
|
+ private int c2me$lastX = Integer.MIN_VALUE;
|
|
+ private int c2me$lastY = Integer.MIN_VALUE;
|
|
+ private int c2me$lastZ = Integer.MIN_VALUE;
|
|
+
|
|
+ private int[] c2me$lastXa;
|
|
+ private int[] c2me$lastYa;
|
|
+ private int[] c2me$lastZa;
|
|
+ private double[] c2me$lastValuea;
|
|
+
|
|
+ @Override
|
|
+ public double c2me$getCached(int x, int y, int z, EvalType evalType) {
|
|
+ if (c2me$lastValuea != null) {
|
|
+ for (int i = 0; i < this.c2me$lastValuea.length; i ++) {
|
|
+ if (c2me$lastXa[i] == x && c2me$lastYa[i] == y && c2me$lastZa[i] == z) {
|
|
+ return c2me$lastValuea[i];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (!Double.isNaN(c2me$lastValue) && c2me$lastX == x && c2me$lastY == y && c2me$lastZ == z) {
|
|
+ return c2me$lastValue;
|
|
+ }
|
|
+
|
|
+ return Double.longBitsToDouble(CACHE_MISS_NAN_BITS);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean c2me$getCached(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ if (c2me$lastValuea != null && Arrays.equals(y, c2me$lastYa) && Arrays.equals(x, c2me$lastXa) && Arrays.equals(z, c2me$lastZa)) {
|
|
+ System.arraycopy(c2me$lastValuea, 0, res, 0, c2me$lastValuea.length);
|
|
+ return true;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(int x, int y, int z, EvalType evalType, double cached) {
|
|
+ c2me$lastValue = cached;
|
|
+ c2me$lastX = x;
|
|
+ c2me$lastY = y;
|
|
+ c2me$lastZ = z;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ if (c2me$lastValuea != null && this.c2me$lastValuea.length == res.length) {
|
|
+ System.arraycopy(res, 0, this.c2me$lastValuea, 0, this.c2me$lastValuea.length);
|
|
+ System.arraycopy(x, 0, this.c2me$lastXa, 0, this.c2me$lastValuea.length);
|
|
+ System.arraycopy(y, 0, this.c2me$lastYa, 0, this.c2me$lastValuea.length);
|
|
+ System.arraycopy(z, 0, this.c2me$lastZa, 0, this.c2me$lastValuea.length);
|
|
+ } else {
|
|
+ this.c2me$lastValuea = Arrays.copyOf(res, res.length);
|
|
+ this.c2me$lastXa = Arrays.copyOf(x, x.length);
|
|
+ this.c2me$lastYa = Arrays.copyOf(y, y.length);
|
|
+ this.c2me$lastZa = Arrays.copyOf(z, z.length);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$getDelegate() {
|
|
+ return this.function;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$withDelegate(DensityFunction delegate) {
|
|
+ this.function = delegate;
|
|
+ return this;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
|
|
CacheOnce(final DensityFunction function) {
|
|
this.function = function;
|
|
@@ -583,34 +885,82 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
- if (context != NoiseChunk.this) {
|
|
- return this.function.compute(context);
|
|
- } else if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
- return this.lastArray[NoiseChunk.this.arrayIndex];
|
|
- } else if (this.lastCounter == NoiseChunk.this.interpolationCounter) {
|
|
- return this.lastValue;
|
|
- } else {
|
|
- this.lastCounter = NoiseChunk.this.interpolationCounter;
|
|
- double d = this.function.compute(context);
|
|
- this.lastValue = d;
|
|
- return d;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ Supplier<Double> run = () -> {
|
|
+ if (context != NoiseChunk.this) {
|
|
+ return this.function.compute(context);
|
|
+ } else if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
+ return this.lastArray[NoiseChunk.this.arrayIndex];
|
|
+ } else if (this.lastCounter == NoiseChunk.this.interpolationCounter) {
|
|
+ return this.lastValue;
|
|
+ } else {
|
|
+ this.lastCounter = NoiseChunk.this.interpolationCounter;
|
|
+ double d = this.function.compute(context);
|
|
+ this.lastValue = d;
|
|
+ return d;
|
|
+ }
|
|
+ };
|
|
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler || context instanceof NoiseChunk) {
|
|
+ return run.get();
|
|
}
|
|
+ int blockX = context.blockX();
|
|
+ int blockY = context.blockY();
|
|
+ int blockZ = context.blockZ();
|
|
+ if (c2me$lastValuea != null) {
|
|
+ for (int i = 0; i < this.c2me$lastValuea.length; i ++) {
|
|
+ if (c2me$lastXa[i] == blockX && c2me$lastYa[i] == blockY && c2me$lastZa[i] == blockZ) {
|
|
+ return c2me$lastValuea[i];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (!Double.isNaN(c2me$lastValue) && c2me$lastX == blockX && c2me$lastY == blockY && c2me$lastZ == blockZ) {
|
|
+ return c2me$lastValue;
|
|
+ }
|
|
+ double sample = this.function.compute(context);
|
|
+ c2me$lastValue = sample;
|
|
+ c2me$lastX = blockX;
|
|
+ c2me$lastY = blockY;
|
|
+ c2me$lastZ = blockZ;
|
|
+
|
|
+ return sample;
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
- if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
- System.arraycopy(this.lastArray, 0, array, 0, array.length);
|
|
- } else {
|
|
- this.wrapped().fillArray(array, contextProvider);
|
|
- if (this.lastArray != null && this.lastArray.length == array.length) {
|
|
- System.arraycopy(array, 0, this.lastArray, 0, array.length);
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ Runnable run = () -> {
|
|
+ if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
+ System.arraycopy(this.lastArray, 0, array, 0, array.length);
|
|
} else {
|
|
- this.lastArray = (double[])array.clone();
|
|
- }
|
|
+ this.wrapped().fillArray(array, contextProvider);
|
|
+ if (this.lastArray != null && this.lastArray.length == array.length) {
|
|
+ System.arraycopy(array, 0, this.lastArray, 0, array.length);
|
|
+ } else {
|
|
+ this.lastArray = (double[]) array.clone();
|
|
+ }
|
|
|
|
- this.lastArrayCounter = NoiseChunk.this.arrayInterpolationCounter;
|
|
+ this.lastArrayCounter = NoiseChunk.this.arrayInterpolationCounter;
|
|
+ }
|
|
+ };
|
|
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler || contextProvider instanceof NoiseChunk) {
|
|
+ run.run();
|
|
+ return;
|
|
+ }
|
|
+ if (contextProvider instanceof EachApplierVanillaInterface ap) {
|
|
+ if (c2me$lastValuea != null && Arrays.equals(ap.getY(), c2me$lastYa) && Arrays.equals(ap.getX(), c2me$lastXa) && Arrays.equals(ap.getZ(), c2me$lastZa)) {
|
|
+ System.arraycopy(c2me$lastValuea, 0, array, 0, c2me$lastValuea.length);
|
|
+ } else {
|
|
+ this.function.fillArray(array, contextProvider);
|
|
+ this.c2me$lastValuea = Arrays.copyOf(array, array.length);
|
|
+ this.c2me$lastXa = ap.getX();
|
|
+ this.c2me$lastYa = ap.getY();
|
|
+ this.c2me$lastZa = ap.getZ();
|
|
+ }
|
|
+ return;
|
|
}
|
|
+ this.function.fillArray(array, contextProvider);
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
@Override
|
|
@@ -624,10 +974,64 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
}
|
|
}
|
|
|
|
- class FlatCache implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
+ class FlatCache implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction, IFastCacheLike { // DivineMC - Density Function Compiler
|
|
private DensityFunction noiseFiller;
|
|
final double[] values;
|
|
final int sizeXZ;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ @Override
|
|
+ public double c2me$getCached(int x, int y, int z, EvalType evalType) {
|
|
+ int i = QuartPos.fromBlock(x);
|
|
+ int j = QuartPos.fromBlock(z);
|
|
+ int k = i - (NoiseChunk.this).firstNoiseX;
|
|
+ int l = j - (NoiseChunk.this).firstNoiseZ;
|
|
+ int m = this.values.length;
|
|
+ if (k >= 0 && l >= 0 && k < m && l < m) {
|
|
+ return this.values[k + l * this.values.length];
|
|
+ } else {
|
|
+ return Double.longBitsToDouble(CACHE_MISS_NAN_BITS);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean c2me$getCached(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ for (int i = 0; i < res.length; i ++) {
|
|
+ int i1 = QuartPos.fromBlock(x[i]);
|
|
+ int j1 = QuartPos.fromBlock(z[i]);
|
|
+ int k = i1 - (NoiseChunk.this).firstNoiseX;
|
|
+ int l = j1 - (NoiseChunk.this).firstNoiseZ;
|
|
+ int m = this.values.length;
|
|
+ if (k >= 0 && l >= 0 && k < m && l < m) {
|
|
+ res[i] = this.values[k + l * this.values.length];
|
|
+ } else {
|
|
+ System.out.println("partial flat cache hit");
|
|
+ return false; // partial hit possible
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(int x, int y, int z, EvalType evalType, double cached) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$getDelegate() {
|
|
+ return this.noiseFiller;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$withDelegate(DensityFunction delegate) {
|
|
+ this.noiseFiller = delegate;
|
|
+ return this;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
|
|
FlatCache(final DensityFunction noiseFiller, final boolean computeValues) {
|
|
this.noiseFiller = noiseFiller;
|
|
@@ -686,7 +1090,7 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
}
|
|
}
|
|
|
|
- public class NoiseInterpolator implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
+ public class NoiseInterpolator implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction, IFastCacheLike { // DivineMC - Density Function Compiler
|
|
double[][] slice0;
|
|
double[][] slice1;
|
|
private DensityFunction noiseFiller;
|
|
@@ -705,6 +1109,104 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
private double valueZ0;
|
|
private double valueZ1;
|
|
private double value;
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ @Override
|
|
+ public double c2me$getCached(int x, int y, int z, EvalType evalType) {
|
|
+ if (evalType == EvalType.INTERPOLATION) {
|
|
+ boolean isInInterpolationLoop = (NoiseChunk.this).interpolating;
|
|
+ if (isInInterpolationLoop) {
|
|
+ if ((NoiseChunk.this).fillingCell) {
|
|
+ int startBlockX = (NoiseChunk.this).cellStartBlockX;
|
|
+ int startBlockY = (NoiseChunk.this).cellStartBlockY;
|
|
+ int startBlockZ = (NoiseChunk.this).cellStartBlockZ;
|
|
+ int horizontalCellBlockCount = (NoiseChunk.this).cellWidth;
|
|
+ int verticalCellBlockCount = (NoiseChunk.this).cellHeight;
|
|
+ int cellBlockX = x - startBlockX;
|
|
+ int cellBlockY = y - startBlockY;
|
|
+ int cellBlockZ = z - startBlockZ;
|
|
+ return Mth.lerp3(
|
|
+ (double) cellBlockX / (double) horizontalCellBlockCount,
|
|
+ (double) cellBlockY / (double) verticalCellBlockCount,
|
|
+ (double) cellBlockZ / (double) horizontalCellBlockCount,
|
|
+ this.noise000,
|
|
+ this.noise100,
|
|
+ this.noise010,
|
|
+ this.noise110,
|
|
+ this.noise001,
|
|
+ this.noise101,
|
|
+ this.noise011,
|
|
+ this.noise111
|
|
+ );
|
|
+ } else {
|
|
+ return this.value;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return CACHE_MISS_NAN_BITS;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean c2me$getCached(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ if (evalType == EvalType.INTERPOLATION) {
|
|
+ boolean isInInterpolationLoop = (NoiseChunk.this).interpolating;
|
|
+ if (isInInterpolationLoop) {
|
|
+ if ((NoiseChunk.this).fillingCell) {
|
|
+ int startBlockX = (NoiseChunk.this).cellStartBlockX;
|
|
+ int startBlockY = (NoiseChunk.this).cellStartBlockY;
|
|
+ int startBlockZ = (NoiseChunk.this).cellStartBlockZ;
|
|
+ double horizontalCellBlockCount = (NoiseChunk.this).cellWidth;
|
|
+ double verticalCellBlockCount = (NoiseChunk.this).cellHeight;
|
|
+ for (int i = 0; i < res.length; i ++) {
|
|
+ int cellBlockX = x[i] - startBlockX;
|
|
+ int cellBlockY = y[i] - startBlockY;
|
|
+ int cellBlockZ = z[i] - startBlockZ;
|
|
+ res[i] = Mth.lerp3(
|
|
+ (double)cellBlockX / horizontalCellBlockCount,
|
|
+ (double)cellBlockY / verticalCellBlockCount,
|
|
+ (double)cellBlockZ / horizontalCellBlockCount,
|
|
+ this.noise000,
|
|
+ this.noise100,
|
|
+ this.noise010,
|
|
+ this.noise110,
|
|
+ this.noise001,
|
|
+ this.noise101,
|
|
+ this.noise011,
|
|
+ this.noise111
|
|
+ );
|
|
+ }
|
|
+ return true;
|
|
+ } else {
|
|
+ Arrays.fill(res, this.value);
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(int x, int y, int z, EvalType evalType, double cached) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void c2me$cache(double[] res, int[] x, int[] y, int[] z, EvalType evalType) {
|
|
+ // nop
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$getDelegate() {
|
|
+ return this.noiseFiller;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DensityFunction c2me$withDelegate(DensityFunction delegate) {
|
|
+ this.noiseFiller = delegate;
|
|
+ return this;
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
|
|
NoiseInterpolator(final DensityFunction noiseFilter) {
|
|
this.noiseFiller = noiseFilter;
|
|
@@ -754,16 +1256,18 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
- if (context != NoiseChunk.this) {
|
|
- return this.noiseFiller.compute(context);
|
|
- } else if (!NoiseChunk.this.interpolating) {
|
|
- throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
- } else {
|
|
- return NoiseChunk.this.fillingCell
|
|
- ? Mth.lerp3(
|
|
- (double)NoiseChunk.this.inCellX / NoiseChunk.this.cellWidth,
|
|
- (double)NoiseChunk.this.inCellY / NoiseChunk.this.cellHeight,
|
|
- (double)NoiseChunk.this.inCellZ / NoiseChunk.this.cellWidth,
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ Supplier<Double> original = () -> {
|
|
+ if (context != NoiseChunk.this) {
|
|
+ return this.noiseFiller.compute(context);
|
|
+ } else if (!NoiseChunk.this.interpolating) {
|
|
+ throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
+ } else {
|
|
+ return NoiseChunk.this.fillingCell
|
|
+ ? Mth.lerp3(
|
|
+ (double) NoiseChunk.this.inCellX / NoiseChunk.this.cellWidth,
|
|
+ (double) NoiseChunk.this.inCellY / NoiseChunk.this.cellHeight,
|
|
+ (double) NoiseChunk.this.inCellZ / NoiseChunk.this.cellWidth,
|
|
this.noise000,
|
|
this.noise100,
|
|
this.noise010,
|
|
@@ -773,8 +1277,45 @@ public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunct
|
|
this.noise011,
|
|
this.noise111
|
|
)
|
|
- : this.value;
|
|
+ : this.value;
|
|
+ }
|
|
+ };
|
|
+ if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler || context instanceof NoiseChunk) {
|
|
+ return original.get();
|
|
}
|
|
+ if (context instanceof NoisePosVanillaInterface vif && vif.getType() == EvalType.INTERPOLATION) {
|
|
+ NoiseChunk field = NoiseChunk.this;
|
|
+ boolean isInInterpolationLoop = field.interpolating;
|
|
+ boolean isSamplingForCaches = field.fillingCell;
|
|
+ if (!isInInterpolationLoop) {
|
|
+ return original.get();
|
|
+ }
|
|
+ int startBlockX = field.cellStartBlockX;
|
|
+ int startBlockY = field.cellStartBlockY;
|
|
+ int startBlockZ = field.cellStartBlockZ;
|
|
+ int horizontalCellBlockCount = field.cellWidth();
|
|
+ int verticalCellBlockCount = field.cellHeight();
|
|
+ int cellBlockX = context.blockX() - startBlockX;
|
|
+ int cellBlockY = context.blockY() - startBlockY;
|
|
+ int cellBlockZ = context.blockZ() - startBlockZ;
|
|
+ return isSamplingForCaches
|
|
+ ? Mth.lerp3(
|
|
+ (double)cellBlockX / (double)horizontalCellBlockCount,
|
|
+ (double)cellBlockY / (double)verticalCellBlockCount,
|
|
+ (double)cellBlockZ / (double)horizontalCellBlockCount,
|
|
+ this.noise000,
|
|
+ this.noise100,
|
|
+ this.noise010,
|
|
+ this.noise110,
|
|
+ this.noise001,
|
|
+ this.noise101,
|
|
+ this.noise011,
|
|
+ this.noise111
|
|
+ ) : this.value;
|
|
+ }
|
|
+
|
|
+ return original.get();
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
@Override
|
|
diff --git a/net/minecraft/world/level/levelgen/RandomState.java b/net/minecraft/world/level/levelgen/RandomState.java
|
|
index f1e089ecfffa40cd794c49db30fcedf138d3fee9..f0f28f433523e0ee9379af5b70cffe8f8c87da5d 100644
|
|
--- a/net/minecraft/world/level/levelgen/RandomState.java
|
|
+++ b/net/minecraft/world/level/levelgen/RandomState.java
|
|
@@ -122,6 +122,41 @@ public final class RandomState {
|
|
this.router.ridges().mapAll(visitor),
|
|
settings.spawnTarget()
|
|
);
|
|
+
|
|
+ // DivineMC start - Density Function Compiler
|
|
+ if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.enableDensityFunctionCompiler) {
|
|
+ com.google.common.base.Stopwatch stopwatch = com.google.common.base.Stopwatch.createStarted();
|
|
+ it.unimi.dsi.fastutil.objects.Reference2ReferenceMap<DensityFunction, DensityFunction> tempCache = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>();
|
|
+ this.router = new NoiseRouter(
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.barrierNoise(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.fluidLevelFloodednessNoise(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.fluidLevelSpreadNoise(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.lavaNoise(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.temperature(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.vegetation(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.continents(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.erosion(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.depth(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.ridges(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.preliminarySurfaceLevel(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.finalDensity(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.veinToggle(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.veinRidged(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.router.veinGap(), tempCache)
|
|
+ );
|
|
+ this.sampler = new Climate.Sampler(
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.sampler.temperature(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.sampler.humidity(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.sampler.continentalness(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.sampler.erosion(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.sampler.depth(), tempCache),
|
|
+ com.ishland.c2me.opts.dfc.common.gen.BytecodeGen.compile(this.sampler.weirdness(), tempCache),
|
|
+ this.sampler.spawnTarget()
|
|
+ );
|
|
+ stopwatch.stop();
|
|
+ System.out.printf("Density function compilation finished in %s%n", stopwatch);
|
|
+ }
|
|
+ // DivineMC end - Density Function Compiler
|
|
}
|
|
|
|
public NormalNoise getOrCreateNoise(ResourceKey<NormalNoise.NoiseParameters> resourceKey) {
|