diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/McToAst.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/McToAst.java index 54cfa78e6..99a127a03 100644 --- a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/McToAst.java +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/McToAst.java @@ -2,7 +2,9 @@ import com.ishland.c2me.opts.dfc.common.ast.binary.AddNode; import com.ishland.c2me.opts.dfc.common.ast.binary.MaxNode; +import com.ishland.c2me.opts.dfc.common.ast.binary.MaxShortNode; import com.ishland.c2me.opts.dfc.common.ast.binary.MinNode; +import com.ishland.c2me.opts.dfc.common.ast.binary.MinShortNode; import com.ishland.c2me.opts.dfc.common.ast.binary.MulNode; import com.ishland.c2me.opts.dfc.common.ast.misc.CacheLikeNode; import com.ishland.c2me.opts.dfc.common.ast.misc.ConstantNode; @@ -29,8 +31,6 @@ import net.minecraft.world.gen.densityfunction.DensityFunctionTypes; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.LongAdder; public class McToAst { @@ -48,8 +48,22 @@ public static AstNode toAst(DensityFunction df) { case DensityFunctionTypes.BinaryOperationLike f -> switch (f.type()) { case ADD -> new AddNode(toAst(f.argument1()), toAst(f.argument2())); case MUL -> new MulNode(toAst(f.argument1()), toAst(f.argument2())); - case MIN -> new MinNode(toAst(f.argument1()), toAst(f.argument2())); - case MAX -> new MaxNode(toAst(f.argument1()), toAst(f.argument2())); + case MIN -> { + double rightMin = f.argument2().minValue(); + if (f.argument1().minValue() < rightMin) { + yield new MinShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMin); + } else { + yield new MinNode(toAst(f.argument1()), toAst(f.argument2())); + } + } + case MAX -> { + double rightMax = f.argument2().maxValue(); + if (f.argument1().maxValue() > rightMax) { + yield new MaxShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMax); + } else { + yield new MaxNode(toAst(f.argument1()), toAst(f.argument2())); + } + } }; case DensityFunctionTypes.BlendDensity f -> toAst(f.input()); case DensityFunctionTypes.Clamp f -> new MaxNode(new ConstantNode(f.minValue()), new MinNode(new ConstantNode(f.maxValue()), toAst(f.input()))); diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxNode.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxNode.java index b23e535ac..8026dd587 100644 --- a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxNode.java +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxNode.java @@ -6,7 +6,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; -public class MaxNode extends AbstractBinaryNode { // missed optimization: left > right.maxValue +public class MaxNode extends AbstractBinaryNode { public MaxNode(AstNode left, AstNode right) { super(left, right); diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxShortNode.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxShortNode.java new file mode 100644 index 000000000..8f5f584b1 --- /dev/null +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MaxShortNode.java @@ -0,0 +1,73 @@ +package com.ishland.c2me.opts.dfc.common.ast.binary; + +import com.ishland.c2me.opts.dfc.common.ast.AstNode; +import com.ishland.c2me.opts.dfc.common.ast.EvalType; +import com.ishland.c2me.opts.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MaxShortNode extends AbstractBinaryNode { + + private final double rightMax; + + public MaxShortNode(AstNode left, AstNode right, double rightMax) { + super(left, right); + this.rightMax = rightMax; + } + + @Override + protected AstNode newInstance(AstNode left, AstNode right) { + return new MaxShortNode(left, right, this.rightMax); + } + + @Override + public double evalSingle(int x, int y, int z, EvalType type) { + double evaled = this.left.evalSingle(x, y, z, type); + return evaled >= this.rightMax ? evaled : Math.max(evaled, this.right.evalSingle(x, y, z, type)); + } + + @Override + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.left.evalMulti(res, x, y, z, type); + for (int i = 0; i < res.length; i++) { + res[i] = res[i] >= this.rightMax ? res[i] : Math.max(res[i], this.right.evalSingle(x[i], y[i], z[i], type)); + } + } + + @Override + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + + Label minLabel = new Label(); + + context.callDelegateSingle(m, leftMethod); + m.dup2(); + m.dconst(this.rightMax); + m.cmpl(Type.DOUBLE_TYPE); + m.iflt(minLabel); + m.areturn(Type.DOUBLE_TYPE); + + m.visitLabel(minLabel); + context.callDelegateSingle(m, rightMethod); + m.invokestatic( + Type.getInternalName(Math.class), + "max", + Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), + false + ); + m.areturn(Type.DOUBLE_TYPE); + } + + @Override + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + context.delegateToSingle(m, localVarConsumer, this); + m.areturn(Type.VOID_TYPE); + } + + @Override + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + throw new UnsupportedOperationException(); + } +} diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinNode.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinNode.java index 8c3856675..77410d3b8 100644 --- a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinNode.java +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinNode.java @@ -6,7 +6,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; -public class MinNode extends AbstractBinaryNode { // missed optimization: left < right.minValue +public class MinNode extends AbstractBinaryNode { public MinNode(AstNode left, AstNode right) { super(left, right); diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinShortNode.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinShortNode.java new file mode 100644 index 000000000..348d58d85 --- /dev/null +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MinShortNode.java @@ -0,0 +1,73 @@ +package com.ishland.c2me.opts.dfc.common.ast.binary; + +import com.ishland.c2me.opts.dfc.common.ast.AstNode; +import com.ishland.c2me.opts.dfc.common.ast.EvalType; +import com.ishland.c2me.opts.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +public class MinShortNode extends AbstractBinaryNode { + + private final double rightMin; + + public MinShortNode(AstNode left, AstNode right, double rightMin) { + super(left, right); + this.rightMin = rightMin; + } + + @Override + protected AstNode newInstance(AstNode left, AstNode right) { + return new MinShortNode(left, right, this.rightMin); + } + + @Override + public double evalSingle(int x, int y, int z, EvalType type) { + double evaled = this.left.evalSingle(x, y, z, type); + return evaled <= this.rightMin ? evaled : Math.min(evaled, this.right.evalSingle(x, y, z, type)); + } + + @Override + public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { + this.left.evalMulti(res, x, y, z, type); + for (int i = 0; i < res.length; i++) { + res[i] = res[i] <= this.rightMin ? res[i] : Math.min(res[i], this.right.evalSingle(x[i], y[i], z[i], type)); + } + } + + @Override + public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + + Label minLabel = new Label(); + + context.callDelegateSingle(m, leftMethod); + m.dup2(); + m.dconst(this.rightMin); + m.cmpg(Type.DOUBLE_TYPE); + m.ifgt(minLabel); + m.areturn(Type.DOUBLE_TYPE); + + m.visitLabel(minLabel); + context.callDelegateSingle(m, rightMethod); + m.invokestatic( + Type.getInternalName(Math.class), + "min", + Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), + false + ); + m.areturn(Type.DOUBLE_TYPE); + } + + @Override + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + context.delegateToSingle(m, localVarConsumer, this); + m.areturn(Type.VOID_TYPE); + } + + @Override + protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { + throw new UnsupportedOperationException(); + } +} diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MulNode.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MulNode.java index cc2853e1c..d3ba863b3 100644 --- a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MulNode.java +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/binary/MulNode.java @@ -3,6 +3,7 @@ import com.ishland.c2me.opts.dfc.common.ast.AstNode; import com.ishland.c2me.opts.dfc.common.ast.EvalType; import com.ishland.c2me.opts.dfc.common.gen.BytecodeGen; +import org.objectweb.asm.Label; import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; @@ -19,36 +20,47 @@ protected AstNode newInstance(AstNode left, AstNode right) { @Override public double evalSingle(int x, int y, int z, EvalType type) { - return this.left.evalSingle(x, y, z, type) * this.right.evalSingle(x, y, z, type); + double evaled = this.left.evalSingle(x, y, z, type); + return evaled == 0.0 ? 0.0 : evaled * this.right.evalSingle(x, y, z, type); } @Override public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) { - double[] res1 = new double[res.length]; this.left.evalMulti(res, x, y, z, type); - this.right.evalMulti(res1, x, y, z, type); - for (int i = 0; i < res1.length; i++) { - res[i] *= res1[i]; + for (int i = 0; i < res.length; i++) { + res[i] = res[i] == 0.0 ? 0.0 : res[i] * this.right.evalSingle(x[i], y[i], z[i], type); } } @Override public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { - super.doBytecodeGenSingle(context, m, localVarConsumer); + String leftMethod = context.newSingleMethod(this.left); + String rightMethod = context.newSingleMethod(this.right); + + Label notZero = new Label(); + + context.callDelegateSingle(m, leftMethod); + m.dup2(); + m.dconst(0.0); + m.cmpl(Type.DOUBLE_TYPE); + m.ifne(notZero); + m.dconst(0.0); + m.areturn(Type.DOUBLE_TYPE); + + m.visitLabel(notZero); + context.callDelegateSingle(m, rightMethod); m.mul(Type.DOUBLE_TYPE); m.areturn(Type.DOUBLE_TYPE); } + @Override + public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { + context.delegateToSingle(m, localVarConsumer, this); + m.areturn(Type.VOID_TYPE); + } + @Override protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) { - m.load(1, InstructionAdapter.OBJECT_TYPE); - m.load(idx, Type.INT_TYPE); - m.dup2(); - m.aload(Type.DOUBLE_TYPE); - m.load(res1, InstructionAdapter.OBJECT_TYPE); - m.load(idx, Type.INT_TYPE); - m.aload(Type.DOUBLE_TYPE); - m.mul(Type.DOUBLE_TYPE); - m.astore(Type.DOUBLE_TYPE); + throw new UnsupportedOperationException(); } } diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/dfvisitor/StripBlending.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/dfvisitor/StripBlending.java new file mode 100644 index 000000000..111b4d863 --- /dev/null +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/dfvisitor/StripBlending.java @@ -0,0 +1,25 @@ +package com.ishland.c2me.opts.dfc.common.ast.dfvisitor; + +import net.minecraft.world.gen.chunk.ChunkNoiseSampler; +import net.minecraft.world.gen.densityfunction.DensityFunction; +import net.minecraft.world.gen.densityfunction.DensityFunctionTypes; + +public class StripBlending implements DensityFunction.DensityFunctionVisitor { + + public static final StripBlending INSTANCE = new StripBlending(); + + private StripBlending() { + } + + @Override + public DensityFunction apply(DensityFunction densityFunction) { + return switch (densityFunction) { + case ChunkNoiseSampler.BlendAlphaDensityFunction f -> DensityFunctionTypes.constant(1.0); + case ChunkNoiseSampler.BlendOffsetDensityFunction f -> DensityFunctionTypes.constant(0.0); + case DensityFunctionTypes.BlendAlpha f -> DensityFunctionTypes.constant(1.0); + case DensityFunctionTypes.BlendOffset f -> DensityFunctionTypes.constant(0.0); + default -> densityFunction; + }; + } + +} diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/spline/SplineAstNode.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/spline/SplineAstNode.java index d27b75e99..d3ed9c2cc 100644 --- a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/spline/SplineAstNode.java +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/ast/spline/SplineAstNode.java @@ -416,34 +416,7 @@ private record ValuesMethodDef(boolean isConst, String generatedMethod, float co @Override public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) { - String singleMethod = context.newSingleMethod(this); - context.doCountedLoop(m, localVarConsumer, idx -> { - m.load(1, InstructionAdapter.OBJECT_TYPE); - m.load(idx, Type.INT_TYPE); - - { - m.load(0, InstructionAdapter.OBJECT_TYPE); - m.load(2, InstructionAdapter.OBJECT_TYPE); - m.load(idx, Type.INT_TYPE); - m.aload(Type.INT_TYPE); - m.load(3, InstructionAdapter.OBJECT_TYPE); - m.load(idx, Type.INT_TYPE); - m.aload(Type.INT_TYPE); - m.load(4, InstructionAdapter.OBJECT_TYPE); - m.load(idx, Type.INT_TYPE); - m.aload(Type.INT_TYPE); - m.load(5, InstructionAdapter.OBJECT_TYPE); - - m.invokevirtual( - context.className, - singleMethod, - BytecodeGen.Context.SINGLE_DESC, - false - ); - } - - m.astore(Type.DOUBLE_TYPE); - }); + context.delegateToSingle(m, localVarConsumer, this); m.areturn(Type.VOID_TYPE); } diff --git a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/gen/BytecodeGen.java b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/gen/BytecodeGen.java index 14c3f7ef5..0f62dc9ba 100644 --- a/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/gen/BytecodeGen.java +++ b/c2me-opts-dfc/src/main/java/com/ishland/c2me/opts/dfc/common/gen/BytecodeGen.java @@ -3,6 +3,7 @@ import com.ishland.c2me.opts.dfc.common.ast.AstNode; import com.ishland.c2me.opts.dfc.common.ast.EvalType; import com.ishland.c2me.opts.dfc.common.ast.McToAst; +import com.ishland.c2me.opts.dfc.common.ast.dfvisitor.StripBlending; import com.ishland.c2me.opts.dfc.common.ast.misc.ConstantNode; import com.ishland.c2me.opts.dfc.common.ast.misc.RootNode; import com.ishland.c2me.opts.dfc.common.util.ArrayCache; @@ -78,7 +79,7 @@ public static DensityFunction compile(DensityFunction densityFunction, Reference AstNode ast = vif.getAstNode(); return new CompiledDensityFunction(compile0(ast), vif.getBlendingFallback()); } - AstNode ast = McToAst.toAst(densityFunction); + AstNode ast = McToAst.toAst(densityFunction.apply(StripBlending.INSTANCE)); if (ast instanceof ConstantNode constantNode) { return DensityFunctionTypes.constant(constantNode.getValue()); } @@ -498,6 +499,37 @@ public void doCountedLoop(InstructionAdapter m, LocalVarConsumer localVarConsume m.visitLabel(end); } + public void delegateToSingle(InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer, AstNode current) { + String singleMethod = this.newSingleMethod(current); + this.doCountedLoop(m, localVarConsumer, idx -> { + m.load(1, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + + { + m.load(0, InstructionAdapter.OBJECT_TYPE); + m.load(2, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(3, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(4, InstructionAdapter.OBJECT_TYPE); + m.load(idx, Type.INT_TYPE); + m.aload(Type.INT_TYPE); + m.load(5, InstructionAdapter.OBJECT_TYPE); + + m.invokevirtual( + this.className, + singleMethod, + BytecodeGen.Context.SINGLE_DESC, + false + ); + } + + m.astore(Type.DOUBLE_TYPE); + }); + } + public void genPostprocessingMethod(String name, Consumer generator) { if (this.postProcessMethods.contains(name)) { return;