mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-12 18:04:01 +00:00
SMID
This commit is contained in:
@@ -70,6 +70,7 @@ import art.arcane.iris.util.common.plugin.VolmitSender;
|
||||
import art.arcane.iris.util.common.plugin.chunk.ChunkTickets;
|
||||
import art.arcane.iris.util.common.scheduling.J;
|
||||
import art.arcane.iris.util.common.misc.ServerProperties;
|
||||
import art.arcane.iris.util.simd.SimdSupport;
|
||||
import art.arcane.volmlib.util.scheduling.Queue;
|
||||
import art.arcane.volmlib.util.scheduling.ShurikenQueue;
|
||||
import io.papermc.lib.PaperLib;
|
||||
@@ -547,6 +548,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
|
||||
private void enable() {
|
||||
PaperLibBootstrap.install();
|
||||
SimdSupport.install();
|
||||
services = new KMap<>();
|
||||
setupAudience();
|
||||
Bindings.setupSentry();
|
||||
|
||||
@@ -193,6 +193,7 @@ public class IrisSettings {
|
||||
public int objectLoaderCacheSize = 4_096;
|
||||
public int tectonicPlateSize = -1;
|
||||
public int mantleCleanupDelay = 200;
|
||||
public boolean simdKernels = true;
|
||||
|
||||
public int getTectonicPlateSize() {
|
||||
if (tectonicPlateSize > 0)
|
||||
|
||||
@@ -215,6 +215,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
|
||||
int fluidHeight = dimension.getFluidHeight();
|
||||
int clampedFluidHeight = Math.min(chunkHeight, fluidHeight);
|
||||
boolean bedrockEnabled = dimension.isBedrock();
|
||||
boolean hideOres = dimension.isHideOresForHiddenOre();
|
||||
ChunkedDataCache<IrisBiome> biomeCache = context.getBiome();
|
||||
ChunkedDataCache<IrisRegion> regionCache = context.getRegion();
|
||||
ChunkedDataCache<BlockData> fluidCache = context.getFluid();
|
||||
@@ -235,8 +236,8 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
|
||||
int topY = Math.min(hf, chunkHeight - 1);
|
||||
BlockData fluid = fluidCache.get(xf, zf);
|
||||
BlockData rock = rockCache.get(xf, zf);
|
||||
boolean hasSurfaceOres = biome.hasSurfaceOres() || region.hasSurfaceOres() || dimension.hasSurfaceOres();
|
||||
boolean hasUndergroundOres = biome.hasUndergroundOres() || region.hasUndergroundOres() || dimension.hasUndergroundOres();
|
||||
boolean hasSurfaceOres = !hideOres && (biome.hasSurfaceOres() || region.hasSurfaceOres() || dimension.hasSurfaceOres());
|
||||
boolean hasUndergroundOres = !hideOres && (biome.hasUndergroundOres() || region.hasUndergroundOres() || dimension.hasUndergroundOres());
|
||||
KList<BlockData> blocks = null;
|
||||
KList<BlockData> fblocks = null;
|
||||
|
||||
|
||||
@@ -25,25 +25,61 @@ import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.iris.util.common.parallel.BurstExecutor;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class IrisPerfectionModifier extends EngineAssignedModifier<BlockData> {
|
||||
private static final BlockData AIR = B.get("AIR");
|
||||
private static final BlockData WATER = B.get("WATER");
|
||||
private static final Map<Material, BlockData> ORE_BASES = buildOreBases();
|
||||
|
||||
public IrisPerfectionModifier(Engine engine) {
|
||||
super(engine, "Perfection");
|
||||
}
|
||||
|
||||
private static Map<Material, BlockData> buildOreBases() {
|
||||
Map<Material, BlockData> map = new EnumMap<>(Material.class);
|
||||
BlockData stone = B.get("STONE");
|
||||
BlockData deepslate = B.get("DEEPSLATE");
|
||||
BlockData netherrack = B.get("NETHERRACK");
|
||||
BlockData blackstone = B.get("BLACKSTONE");
|
||||
map.put(Material.COAL_ORE, stone);
|
||||
map.put(Material.COPPER_ORE, stone);
|
||||
map.put(Material.IRON_ORE, stone);
|
||||
map.put(Material.GOLD_ORE, stone);
|
||||
map.put(Material.REDSTONE_ORE, stone);
|
||||
map.put(Material.LAPIS_ORE, stone);
|
||||
map.put(Material.DIAMOND_ORE, stone);
|
||||
map.put(Material.EMERALD_ORE, stone);
|
||||
map.put(Material.DEEPSLATE_COAL_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_COPPER_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_IRON_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_GOLD_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_REDSTONE_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_LAPIS_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_DIAMOND_ORE, deepslate);
|
||||
map.put(Material.DEEPSLATE_EMERALD_ORE, deepslate);
|
||||
map.put(Material.NETHER_GOLD_ORE, netherrack);
|
||||
map.put(Material.NETHER_QUARTZ_ORE, netherrack);
|
||||
map.put(Material.ANCIENT_DEBRIS, netherrack);
|
||||
map.put(Material.GILDED_BLACKSTONE, blackstone);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModify(int x, int z, Hunk<BlockData> output, boolean multicore, ChunkContext context) {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
if (getDimension().isHideOresForHiddenOre()) {
|
||||
hideOres(output, multicore);
|
||||
}
|
||||
AtomicBoolean changed = new AtomicBoolean(true);
|
||||
int passes = 0;
|
||||
AtomicInteger changes = new AtomicInteger();
|
||||
@@ -123,6 +159,29 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<BlockData> {
|
||||
getEngine().getMetrics().getPerfection().put(p.getMilliseconds());
|
||||
}
|
||||
|
||||
private void hideOres(Hunk<BlockData> output, boolean multicore) {
|
||||
BurstExecutor burst = burst().burst(multicore);
|
||||
int height = output.getHeight();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int finalI = i;
|
||||
burst.queue(() -> {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
for (int k = height - 1; k >= 0; k--) {
|
||||
BlockData block = output.get(finalI, k, j);
|
||||
if (block == null) {
|
||||
continue;
|
||||
}
|
||||
BlockData base = ORE_BASES.get(block.getMaterial());
|
||||
if (base != null) {
|
||||
output.set(finalI, k, j, base);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
burst.complete();
|
||||
}
|
||||
|
||||
private int getHeight(Hunk<BlockData> output, int x, int z) {
|
||||
for (int i = output.getHeight() - 1; i >= 0; i--) {
|
||||
BlockData b = output.get(x, i, z);
|
||||
|
||||
@@ -140,6 +140,8 @@ public class IrisDimension extends IrisRegistrant {
|
||||
private boolean debugSmartBore = false;
|
||||
@Desc("Generate decorations or not")
|
||||
private boolean decorate = true;
|
||||
@Desc("When true, every ore block the generator would place (terrain ores, deposits, ores inside objects) is replaced with its base material such as stone, deepslate or netherrack, so drop-control plugins like HiddenOre can manage ore rewards themselves")
|
||||
private boolean hideOresForHiddenOre = false;
|
||||
@Desc("Use post processing or not")
|
||||
private boolean postProcessing = true;
|
||||
@Desc("Add slabs in post processing")
|
||||
|
||||
@@ -2,6 +2,7 @@ package art.arcane.iris.util.project.context;
|
||||
|
||||
import art.arcane.iris.util.project.stream.ProceduralStream;
|
||||
import art.arcane.iris.util.project.stream.utility.ChunkFillableDoubleStream2D;
|
||||
import art.arcane.iris.util.simd.SimdSupport;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -56,9 +57,7 @@ public class ChunkedDoubleDataCache {
|
||||
if (stream instanceof ChunkFillableDoubleStream2D cachedStream) {
|
||||
cachedStream.fillChunkDoubles(x, z, data);
|
||||
if (roundedTarget != null) {
|
||||
for (int index = 0; index < 256; index++) {
|
||||
roundedTarget[index] = (int) Math.round(data[index]);
|
||||
}
|
||||
SimdSupport.kernels().roundToInt(data, roundedTarget, 256);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
public final class ScalarSimdKernels implements SimdKernels {
|
||||
@Override
|
||||
public String describe() {
|
||||
return "scalar";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void roundToInt(double[] source, int[] target, int length) {
|
||||
for (int index = 0; index < length; index++) {
|
||||
target[index] = (int) Math.round(source[index]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sum(double[] values, int length) {
|
||||
double total = 0D;
|
||||
for (int index = 0; index < length; index++) {
|
||||
total += values[index];
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double max(double[] values, int length) {
|
||||
double best = Double.NEGATIVE_INFINITY;
|
||||
for (int index = 0; index < length; index++) {
|
||||
if (values[index] > best) {
|
||||
best = values[index];
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
public interface SimdKernels {
|
||||
String describe();
|
||||
|
||||
void roundToInt(double[] source, int[] target, int length);
|
||||
|
||||
double sum(double[] values, int length);
|
||||
|
||||
double max(double[] values, int length);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
|
||||
public final class SimdSupport {
|
||||
private static final String VECTOR_MODULE = "jdk.incubator.vector";
|
||||
private static final boolean MODULE_PRESENT = ModuleLayer.boot().findModule(VECTOR_MODULE).isPresent();
|
||||
private static final SimdKernels KERNELS = selectKernels();
|
||||
|
||||
private SimdSupport() {
|
||||
}
|
||||
|
||||
public static SimdKernels kernels() {
|
||||
return KERNELS;
|
||||
}
|
||||
|
||||
public static boolean isVectorModulePresent() {
|
||||
return MODULE_PRESENT;
|
||||
}
|
||||
|
||||
public static boolean isVectorized() {
|
||||
return !(KERNELS instanceof ScalarSimdKernels);
|
||||
}
|
||||
|
||||
public static void install() {
|
||||
if (isVectorized()) {
|
||||
Iris.info("SIMD: vector kernels enabled (" + KERNELS.describe() + ")");
|
||||
} else if (!MODULE_PRESENT) {
|
||||
Iris.info("SIMD: scalar kernels active; add --add-modules " + VECTOR_MODULE + " to JVM flags to enable vectorized generation kernels");
|
||||
} else {
|
||||
Iris.info("SIMD: vector kernels disabled (performance.simdKernels=false)");
|
||||
}
|
||||
}
|
||||
|
||||
public static SimdKernels createVectorKernels() {
|
||||
if (!MODULE_PRESENT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return (SimdKernels) Class.forName("art.arcane.iris.util.simd.VectorSimdKernels").getDeclaredConstructor().newInstance();
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static SimdKernels selectKernels() {
|
||||
if (!simdEnabledInSettings()) {
|
||||
return new ScalarSimdKernels();
|
||||
}
|
||||
|
||||
SimdKernels vector = createVectorKernels();
|
||||
return vector == null ? new ScalarSimdKernels() : vector;
|
||||
}
|
||||
|
||||
private static boolean simdEnabledInSettings() {
|
||||
try {
|
||||
return IrisSettings.get().getPerformance().isSimdKernels();
|
||||
} catch (Throwable e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
import jdk.incubator.vector.DoubleVector;
|
||||
import jdk.incubator.vector.IntVector;
|
||||
import jdk.incubator.vector.VectorMask;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorShape;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
public final class VectorSimdKernels implements SimdKernels {
|
||||
private static final VectorSpecies<Double> DOUBLE_SPECIES = DoubleVector.SPECIES_PREFERRED;
|
||||
private static final VectorSpecies<Integer> INT_SPECIES = VectorSpecies.of(int.class, VectorShape.forBitSize(DOUBLE_SPECIES.length() * Integer.SIZE));
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return DOUBLE_SPECIES.length() + "x64-bit lanes, " + DOUBLE_SPECIES.vectorShape();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void roundToInt(double[] source, int[] target, int length) {
|
||||
int lanes = DOUBLE_SPECIES.length();
|
||||
int bound = DOUBLE_SPECIES.loopBound(length);
|
||||
int index = 0;
|
||||
for (; index < bound; index += lanes) {
|
||||
DoubleVector shifted = DoubleVector.fromArray(DOUBLE_SPECIES, source, index).add(0.5D);
|
||||
IntVector truncated = (IntVector) shifted.convertShape(VectorOperators.D2I, INT_SPECIES, 0);
|
||||
DoubleVector truncatedBack = (DoubleVector) truncated.convertShape(VectorOperators.I2D, DOUBLE_SPECIES, 0);
|
||||
VectorMask<Integer> needsDecrement = shifted.lt(truncatedBack).cast(INT_SPECIES);
|
||||
truncated.sub(1, needsDecrement).intoArray(target, index);
|
||||
}
|
||||
|
||||
for (; index < length; index++) {
|
||||
target[index] = (int) Math.round(source[index]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sum(double[] values, int length) {
|
||||
int lanes = DOUBLE_SPECIES.length();
|
||||
int bound = DOUBLE_SPECIES.loopBound(length);
|
||||
DoubleVector accumulator = DoubleVector.zero(DOUBLE_SPECIES);
|
||||
int index = 0;
|
||||
for (; index < bound; index += lanes) {
|
||||
accumulator = accumulator.add(DoubleVector.fromArray(DOUBLE_SPECIES, values, index));
|
||||
}
|
||||
|
||||
double total = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
for (; index < length; index++) {
|
||||
total += values[index];
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double max(double[] values, int length) {
|
||||
int lanes = DOUBLE_SPECIES.length();
|
||||
int bound = DOUBLE_SPECIES.loopBound(length);
|
||||
DoubleVector accumulator = DoubleVector.broadcast(DOUBLE_SPECIES, Double.NEGATIVE_INFINITY);
|
||||
int index = 0;
|
||||
for (; index < bound; index += lanes) {
|
||||
accumulator = accumulator.max(DoubleVector.fromArray(DOUBLE_SPECIES, values, index));
|
||||
}
|
||||
|
||||
double best = accumulator.reduceLanes(VectorOperators.MAX);
|
||||
for (; index < length; index++) {
|
||||
if (values[index] > best) {
|
||||
best = values[index];
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class SimdKernelsTest {
|
||||
private static final double[] BOUNDARY_VALUES = {
|
||||
0D, 0.5D, -0.5D, 1.5D, -1.5D, 2.5D, -2.5D, 63.5D, -63.5D, 64.49999D, -64.49999D,
|
||||
0.4999999D, -0.4999999D, 255.5D, -255.5D, 319.999D, -64D, 2031.5D, -2031.5D, 0.0001D, -0.0001D
|
||||
};
|
||||
|
||||
private static double[] terrainLikeValues(int length) {
|
||||
Random random = new Random(42L);
|
||||
double[] values = new double[length];
|
||||
for (int index = 0; index < length; index++) {
|
||||
values[index] = (random.nextDouble() - 0.5D) * 8192D;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarRoundToIntMatchesMathRound() {
|
||||
ScalarSimdKernels kernels = new ScalarSimdKernels();
|
||||
int[] rounded = new int[BOUNDARY_VALUES.length];
|
||||
kernels.roundToInt(BOUNDARY_VALUES, rounded, BOUNDARY_VALUES.length);
|
||||
for (int index = 0; index < BOUNDARY_VALUES.length; index++) {
|
||||
assertEquals("index " + index + " value " + BOUNDARY_VALUES[index], (int) Math.round(BOUNDARY_VALUES[index]), rounded[index]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarSumAndMaxMatchManualLoop() {
|
||||
ScalarSimdKernels kernels = new ScalarSimdKernels();
|
||||
double[] values = terrainLikeValues(256);
|
||||
double expectedSum = 0D;
|
||||
double expectedMax = Double.NEGATIVE_INFINITY;
|
||||
for (double value : values) {
|
||||
expectedSum += value;
|
||||
if (value > expectedMax) {
|
||||
expectedMax = value;
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(expectedSum, kernels.sum(values, values.length), 0D);
|
||||
assertEquals(expectedMax, kernels.max(values, values.length), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorRoundToIntMatchesScalarOnBoundaries() {
|
||||
SimdKernels vector = requireVectorKernels();
|
||||
ScalarSimdKernels scalar = new ScalarSimdKernels();
|
||||
int[] vectorRounded = new int[BOUNDARY_VALUES.length];
|
||||
int[] scalarRounded = new int[BOUNDARY_VALUES.length];
|
||||
vector.roundToInt(BOUNDARY_VALUES, vectorRounded, BOUNDARY_VALUES.length);
|
||||
scalar.roundToInt(BOUNDARY_VALUES, scalarRounded, BOUNDARY_VALUES.length);
|
||||
for (int index = 0; index < BOUNDARY_VALUES.length; index++) {
|
||||
assertEquals("index " + index + " value " + BOUNDARY_VALUES[index], scalarRounded[index], vectorRounded[index]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorRoundToIntMatchesScalarOnTerrainSweep() {
|
||||
SimdKernels vector = requireVectorKernels();
|
||||
ScalarSimdKernels scalar = new ScalarSimdKernels();
|
||||
for (int length : new int[]{256, 251, 1, 7}) {
|
||||
double[] values = terrainLikeValues(length);
|
||||
int[] vectorRounded = new int[length];
|
||||
int[] scalarRounded = new int[length];
|
||||
vector.roundToInt(values, vectorRounded, length);
|
||||
scalar.roundToInt(values, scalarRounded, length);
|
||||
for (int index = 0; index < length; index++) {
|
||||
assertEquals("length " + length + " index " + index + " value " + values[index], scalarRounded[index], vectorRounded[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorSumMatchesScalarWithinTolerance() {
|
||||
SimdKernels vector = requireVectorKernels();
|
||||
ScalarSimdKernels scalar = new ScalarSimdKernels();
|
||||
for (int length : new int[]{256, 251, 1, 7}) {
|
||||
double[] values = terrainLikeValues(length);
|
||||
double expected = scalar.sum(values, length);
|
||||
double actual = vector.sum(values, length);
|
||||
assertEquals("length " + length, expected, actual, Math.max(1e-9D, Math.abs(expected) * 1e-12D));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorMaxMatchesScalarExactly() {
|
||||
SimdKernels vector = requireVectorKernels();
|
||||
ScalarSimdKernels scalar = new ScalarSimdKernels();
|
||||
for (int length : new int[]{256, 251, 1, 7}) {
|
||||
double[] values = terrainLikeValues(length);
|
||||
assertEquals("length " + length, scalar.max(values, length), vector.max(values, length), 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorMaxOfAllZeroWeightsIsZero() {
|
||||
SimdKernels vector = requireVectorKernels();
|
||||
double[] weights = new double[256];
|
||||
assertEquals(0D, vector.max(weights, weights.length), 0D);
|
||||
assertEquals(0D, vector.sum(weights, weights.length), 0D);
|
||||
}
|
||||
|
||||
private static SimdKernels requireVectorKernels() {
|
||||
Assume.assumeTrue("jdk.incubator.vector module not present in test JVM", SimdSupport.isVectorModulePresent());
|
||||
SimdKernels vector = SimdSupport.createVectorKernels();
|
||||
assertNotNull("vector kernels failed to instantiate despite module being present", vector);
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user