From ea5589b1f031f2135a1c4d22018c347560edaddb Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sun, 15 Sep 2024 17:00:56 +0200 Subject: [PATCH] Introduce basic support for Vector API (#2890) * Introduce basic support for Vector API * add modules to javadoc too * add assumption comments --- build.gradle.kts | 2 +- buildSrc/src/main/kotlin/CommonJavaConfig.kt | 3 + .../core/configuration/Settings.java | 5 + .../core/extent/filter/CountFilter.java | 10 +- .../core/extent/filter/LinkedFilter.java | 36 ++++++- .../core/extent/filter/MaskFilter.java | 52 +++++++++- .../extent/filter/block/CharFilterBlock.java | 15 +-- .../core/internal/simd/SimdSupport.java | 99 +++++++++++++++++++ .../simd/VectorizedCharFilterBlock.java | 33 +++++++ .../core/internal/simd/VectorizedFilter.java | 8 ++ .../core/internal/simd/VectorizedMask.java | 40 ++++++++ .../core/queue/IQueueExtent.java | 9 +- .../implementation/ParallelQueueExtent.java | 6 +- .../sk89q/worldedit/extent/MaskingExtent.java | 22 ++++- .../sk89q/worldedit/function/mask/Mask.java | 18 ++++ 15 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java diff --git a/build.gradle.kts b/build.gradle.kts index 5c8ce8016..afe45594c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -91,7 +91,7 @@ tasks { minecraftVersion(it) pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } .toTypedArray()) - jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true") + jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true", "--add-modules=jdk.incubator.vector") group = "run paper" runDirectory.set(file("run-$it")) } diff --git a/buildSrc/src/main/kotlin/CommonJavaConfig.kt b/buildSrc/src/main/kotlin/CommonJavaConfig.kt index 155321fbc..ea921709c 100644 --- a/buildSrc/src/main/kotlin/CommonJavaConfig.kt +++ b/buildSrc/src/main/kotlin/CommonJavaConfig.kt @@ -28,6 +28,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean options.isDeprecation = true options.encoding = "UTF-8" options.compilerArgs.add("-parameters") + options.compilerArgs.add("--add-modules=jdk.incubator.vector") } configurations.all { @@ -51,12 +52,14 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean tasks.withType().configureEach { (options as StandardJavadocDocletOptions).apply { addStringOption("Xdoclint:none", "-quiet") + addStringOption("-add-modules", "jdk.incubator.vector") tags( "apiNote:a:API Note:", "implSpec:a:Implementation Requirements:", "implNote:a:Implementation Note:" ) options.encoding = "UTF-8" + links( "https://jd.advntr.dev/api/latest/", "https://logging.apache.org/log4j/2.x/javadoc/log4j-api/", diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 4db469187..d35219bec 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -675,6 +675,11 @@ public class Settings extends Config { }) public boolean ALLOW_TICK_FLUIDS = false; + @Comment({ + "Whether FAWE should use the incubator Vector API to accelerate some operations" + }) + public boolean USE_VECTOR_API = false; + } @Comment({"Web/HTTP connection related settings"}) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java index 72ee73013..f6844e684 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/CountFilter.java @@ -1,8 +1,10 @@ package com.fastasyncworldedit.core.extent.filter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; +import jdk.incubator.vector.ShortVector; -public class CountFilter extends ForkedFilter { +public class CountFilter extends ForkedFilter implements VectorizedFilter { private int total; @@ -33,4 +35,10 @@ public class CountFilter extends ForkedFilter { return total; } + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + total += set.length(); + return set; + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java index 25627c451..a600fc180 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/LinkedFilter.java @@ -2,7 +2,9 @@ package com.fastasyncworldedit.core.extent.filter; import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.fastasyncworldedit.core.queue.Filter; +import jdk.incubator.vector.ShortVector; /** * Filter which links two Filters together for single-filter-input operations. @@ -10,10 +12,18 @@ import com.fastasyncworldedit.core.queue.Filter; * @param Parent which extends Filter * @param Child which extends Filter */ -public final class LinkedFilter extends DelegateFilter { +public sealed class LinkedFilter extends DelegateFilter { private final S child; + @SuppressWarnings({"unchecked", "rawtypes"}) // we defeated the type system + public static LinkedFilter of(T parent, S child) { + if (parent instanceof VectorizedFilter p && child instanceof VectorizedFilter c) { + return new VectorizedLinkedFilter(p, c); + } + return new LinkedFilter<>(parent, child); + } + public LinkedFilter(T parent, S child) { super(parent); this.child = child; @@ -30,8 +40,30 @@ public final class LinkedFilter extends Dele } @Override - public LinkedFilter, Filter> newInstance(Filter other) { + public LinkedFilter, ? extends Filter> newInstance(Filter other) { return new LinkedFilter<>(this, other); } + private final static class VectorizedLinkedFilter + extends LinkedFilter implements VectorizedFilter { + + public VectorizedLinkedFilter(final T parent, final S child) { + super(parent, child); + } + + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + ShortVector res = getParent().applyVector(get, set); + return getChild().applyVector(get, res); + } + + @Override + public LinkedFilter, Filter> newInstance(Filter other) { + if (other instanceof VectorizedFilter o) { + return new VectorizedLinkedFilter(this, o); + } + return new LinkedFilter<>(this, other); + } + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java index 9284f8378..ac31938e2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/MaskFilter.java @@ -2,10 +2,17 @@ package com.fastasyncworldedit.core.extent.filter; import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; +import com.fastasyncworldedit.core.internal.simd.VectorizedMask; import com.fastasyncworldedit.core.queue.Filter; import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** @@ -15,8 +22,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class MaskFilter extends DelegateFilter { - private final Mask mask; - private final AtomicInteger changes; + final Mask mask; + final AtomicInteger changes; public MaskFilter(T other, Mask root) { this(other, root, new AtomicInteger()); @@ -60,4 +67,45 @@ public class MaskFilter extends DelegateFilter { return new MaskFilter<>(getParent().fork(), mask.copy(), changes); } + public static class VectorizedMaskFilter extends MaskFilter implements VectorizedFilter { + + private final VectorizedMask vectorizedMask; + + public VectorizedMaskFilter(final T other, final Mask root) { + super(other, root); + this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask"); + } + + public VectorizedMaskFilter(final T other, final Mask root, AtomicInteger changes) { + super(other, root, changes); + this.vectorizedMask = Objects.requireNonNull(SimdSupport.vectorizedTargetMask(root), "invalid vectorizable mask"); + } + + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + final T parent = getParent(); + VectorMask masked = vectorizedMask.compareVector(set, get); + ShortVector res = parent.applyVector(get, set); + res = set.blend(res, masked); + VectorMask changed = res.compare(VectorOperators.NE, set); + changes.getAndAdd(changed.trueCount()); + return res; + } + + @Override + public MaskFilter newInstance(final Filter other) { + if (other instanceof VectorizedFilter o) { + return new VectorizedMaskFilter<>(o, mask); + } + return super.newInstance(other); + } + + @SuppressWarnings("unchecked") + @Override + public Filter fork() { + return new VectorizedMaskFilter<>((T) getParent().fork(), mask.copy(), changes); + } + + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java index 74571af05..cafd4513a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/CharFilterBlock.java @@ -23,12 +23,14 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.registry.BlockMaterial; import org.enginehub.linbus.tree.LinCompoundTag; +import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nonnull; import javax.annotation.Nullable; import static com.sk89q.worldedit.world.block.BlockTypesCache.states; +@ApiStatus.NonExtendable public class CharFilterBlock extends ChunkFilterBlock { private static final SetDelegate FULL = (block, value) -> block.setArr[block.index] = value; @@ -38,10 +40,10 @@ public class CharFilterBlock extends ChunkFilterBlock { private int minLayer; private CharGetBlocks get; private IChunkSet set; - private char[] getArr; + protected char[] getArr; @Nullable - private char[] setArr; - private SetDelegate delegate; + protected char[] setArr; + protected SetDelegate delegate; // local private int layer; private int index; @@ -172,7 +174,7 @@ public class CharFilterBlock extends ChunkFilterBlock { } @Override - public synchronized final void filter(Filter filter) { + public synchronized void filter(Filter filter) { for (y = 0, index = 0; y < 16; y++) { for (z = 0; z < 16; z++) { for (x = 0; x < 16; x++, index++) { @@ -395,7 +397,7 @@ public class CharFilterBlock extends ChunkFilterBlock { } //Set delegate - private SetDelegate initSet() { + protected final SetDelegate initSet() { setArr = set.load(layer); return delegate = FULL; } @@ -427,7 +429,8 @@ public class CharFilterBlock extends ChunkFilterBlock { return getExtent().setBiome(x, y, z, biome); } - private interface SetDelegate { + @ApiStatus.Internal + protected interface SetDelegate { void set(@Nonnull CharFilterBlock block, char value); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java new file mode 100644 index 000000000..23d135fa6 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/SimdSupport.java @@ -0,0 +1,99 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.extent.filter.block.DelegateFilter; +import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask; +import com.fastasyncworldedit.core.queue.Filter; +import com.sk89q.worldedit.function.mask.InverseSingleBlockStateMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorOperators; + +import javax.annotation.Nullable; + +public class SimdSupport { + + private static final boolean VECTOR_API_PRESENT; + + static { + boolean vectorApiPresent = false; + try { + Class.forName("jdk.incubator.vector.Vector"); + vectorApiPresent = true; + } catch (ClassNotFoundException ignored) { + } + VECTOR_API_PRESENT = vectorApiPresent; + if (!VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API) { + LogManagerCompat.getLogger() + .warn("FAWE use-vector-api is enabled but --add-modules=jdk.incubator.vector is not set."); + } + } + + public static boolean useVectorApi() { + return VECTOR_API_PRESENT && Settings.settings().EXPERIMENTAL.USE_VECTOR_API; + } + + public static @Nullable VectorizedMask vectorizedTargetMask(Mask mask) { + if (!useVectorApi()) { + return null; + } + return switch (mask) { + case SingleBlockStateMask single -> vectorizedTargetMask(single.getBlockState().getOrdinalChar()); + case InverseSingleBlockStateMask inverse -> vectorizedTargetMaskInverse(inverse.getBlockState().getOrdinalChar()); + default -> null; + }; + } + + private static VectorizedMask vectorizedTargetMask(char ordinal) { + return (set, get) -> get.compare(VectorOperators.EQ, (short) ordinal); + } + + private static VectorizedMask vectorizedTargetMaskInverse(char ordinal) { + return (set, get) -> get.compare(VectorOperators.NE, (short) ordinal); + } + + public static @Nullable VectorizedFilter vectorizedPattern(Pattern pattern) { + if (!useVectorApi()) { + return null; + } + return switch (pattern) { + case BaseBlock block -> { + if (block.getNbtReference() == null) { + yield new VectorizedPattern<>(block, block.getOrdinalChar()); + } + yield null; + } + case BlockStateHolder blockStateHolder -> new VectorizedPattern<>( + blockStateHolder, + blockStateHolder.getOrdinalChar() + ); + default -> null; + }; + } + + private static final class VectorizedPattern extends DelegateFilter implements VectorizedFilter { + + private final char ordinal; + + public VectorizedPattern(final T parent, char ordinal) { + super(parent); + this.ordinal = ordinal; + } + + @Override + public ShortVector applyVector(final ShortVector get, final ShortVector set) { + return ShortVector.broadcast(ShortVector.SPECIES_PREFERRED, ordinal); + } + + @Override + public Filter newInstance(final Filter other) { + return new VectorizedPattern<>(other, ordinal); + } + + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java new file mode 100644 index 000000000..5c15da22a --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedCharFilterBlock.java @@ -0,0 +1,33 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; +import com.fastasyncworldedit.core.queue.Filter; +import com.sk89q.worldedit.extent.Extent; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorSpecies; + +public class VectorizedCharFilterBlock extends CharFilterBlock { + + public VectorizedCharFilterBlock(final Extent extent) { + super(extent); + } + + @Override + public synchronized void filter(final Filter filter) { + if (!(filter instanceof VectorizedFilter vecFilter)) { + throw new IllegalStateException("Unexpected VectorizedCharFilterBlock " + filter); + } + final VectorSpecies species = ShortVector.SPECIES_PREFERRED; + initSet(); // set array is null before + char[] setArr = this.setArr; + assert setArr != null; + char[] getArr = this.getArr; + // assume setArr.length == getArr.length == 4096 + for (int i = 0; i < 4096; i += species.length()) { + ShortVector set = ShortVector.fromCharArray(species, setArr, i); + ShortVector get = ShortVector.fromCharArray(species, getArr, i); + ShortVector res = vecFilter.applyVector(get, set); + res.intoCharArray(setArr, i); + } + } +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java new file mode 100644 index 000000000..7357bbd59 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedFilter.java @@ -0,0 +1,8 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.queue.Filter; +import jdk.incubator.vector.ShortVector; + +public interface VectorizedFilter extends Filter { + ShortVector applyVector(ShortVector get, ShortVector set); +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java new file mode 100644 index 000000000..0f453f32b --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/simd/VectorizedMask.java @@ -0,0 +1,40 @@ +package com.fastasyncworldedit.core.internal.simd; + +import com.fastasyncworldedit.core.queue.IChunk; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorSpecies; + +public interface VectorizedMask { + + default void processChunks(IChunk chunk, IChunkGet get, IChunkSet set) { + for (int layer = get.getMinSectionPosition(); layer <= get.getMaxSectionPosition(); layer++) { + final char[] sectionSet = set.loadIfPresent(layer); + if (sectionSet == null) { + continue; + } + final char[] sectionGet = get.load(layer); + processSection(layer, sectionSet, sectionGet); + } + } + + default void processSection(int layer, char[] set, char[] get) { + final VectorSpecies species = ShortVector.SPECIES_PREFERRED; + // assume that set.length % species.elementSize() == 0 + for (int i = 0; i < set.length; i += species.length()) { + ShortVector vectorSet = ShortVector.fromCharArray(species, set, i); + ShortVector vectorGet = ShortVector.fromCharArray(species, get, i); + vectorSet = processVector(vectorSet, vectorGet); + vectorSet.intoCharArray(set, i); + } + } + + default ShortVector processVector(ShortVector set, ShortVector get) { + return set.blend(0, compareVector(set, get).not()); + } + + VectorMask compareVector(ShortVector set, ShortVector get); + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java index 3f104b35d..ee3740411 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java @@ -2,6 +2,9 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.IBatchProcessorHolder; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedCharFilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; @@ -146,7 +149,11 @@ public interface IQueueExtent extends Flushable, Trimable, ICh if (newChunk != null) { chunk = newChunk; if (block == null) { - block = this.createFilterBlock(); + if (SimdSupport.useVectorApi() && filter instanceof VectorizedFilter) { + block = new VectorizedCharFilterBlock(this); + } else { + block = this.createFilterBlock(); + } } block.initChunk(chunkX, chunkZ); chunk.filterBlocks(filter, block, region, full); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 1b56afdbc..f197524f3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -14,6 +14,8 @@ import com.fastasyncworldedit.core.extent.processor.BatchProcessorHolder; import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor; import com.fastasyncworldedit.core.function.mask.BlockMaskBuilder; import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; @@ -223,7 +225,9 @@ public class ParallelQueueExtent extends PassthroughExtent { @Override public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException { - return this.changes = apply(region, new LinkedFilter<>(pattern, new CountFilter()), true).getChild().getTotal(); + VectorizedFilter vectorizedPattern = SimdSupport.vectorizedPattern(pattern); + var filter = LinkedFilter.of(vectorizedPattern == null ? pattern : vectorizedPattern, new CountFilter()); + return this.changes = apply(region, filter, true).getChild().getTotal(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 0abc80153..77f9aa1c7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -24,6 +24,8 @@ import com.fastasyncworldedit.core.extent.filter.block.CharFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedMask; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunk; @@ -35,6 +37,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; +import javax.annotation.Nullable; import java.util.function.LongFunction; import static com.google.common.base.Preconditions.checkNotNull; @@ -47,6 +50,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce //FAWE start private final LongFunction getOrCreateFilterBlock; private Mask mask; + private @Nullable VectorizedMask vectorizedMask; //FAWE end /** @@ -59,16 +63,23 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce super(extent); checkNotNull(mask); this.mask = mask; + this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask); //FAWE start this.getOrCreateFilterBlock = FaweCache.INSTANCE.createMainThreadSafeCache(() -> new CharFilterBlock(getExtent())); //FAWE end } //FAWE start - private MaskingExtent(Extent extent, Mask mask, LongFunction getOrCreateFilterBlock) { + private MaskingExtent( + Extent extent, + Mask mask, + LongFunction getOrCreateFilterBlock, + @Nullable VectorizedMask vectorizedMask + ) { super(extent); checkNotNull(mask); this.mask = mask; + this.vectorizedMask = vectorizedMask; this.getOrCreateFilterBlock = getOrCreateFilterBlock; } //FAWE end @@ -90,6 +101,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce public void setMask(Mask mask) { checkNotNull(mask); this.mask = mask; + this.vectorizedMask = SimdSupport.vectorizedTargetMask(mask); } //FAWE start @@ -105,6 +117,10 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce @Override public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + if (this.vectorizedMask != null) { + this.vectorizedMask.processChunks(chunk, get, set); + return set; + } final ChunkFilterBlock filter = getOrCreateFilterBlock.apply(Thread.currentThread().getId()); filter.initChunk(chunk.getX(), chunk.getZ()); return filter.filter(chunk, get, set, this); @@ -122,12 +138,12 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce if (child == getExtent()) { return this; } - return new MaskingExtent(child, this.mask.copy(), this.getOrCreateFilterBlock); + return new MaskingExtent(child, this.mask.copy(), this.getOrCreateFilterBlock, this.vectorizedMask); } @Override public Filter fork() { - return new MaskingExtent(getExtent(), this.mask.copy(), this.getOrCreateFilterBlock); + return new MaskingExtent(getExtent(), this.mask.copy(), this.getOrCreateFilterBlock, this.vectorizedMask); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 94141278d..85e85a8c2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -22,7 +22,11 @@ package com.sk89q.worldedit.function.mask; import com.fastasyncworldedit.core.extent.filter.MaskFilter; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.fastasyncworldedit.core.function.mask.InverseMask; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; +import com.fastasyncworldedit.core.internal.simd.VectorizedMask; import com.fastasyncworldedit.core.queue.Filter; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import javax.annotation.Nullable; @@ -64,7 +68,21 @@ public interface Mask { return null; } + @SuppressWarnings({"unchecked", "rawtypes"}) default MaskFilter toFilter(T filter) { + final VectorizedMask mask = SimdSupport.vectorizedTargetMask(this); + if (mask != null) { + VectorizedFilter vectorizedFilter = null; + if (filter instanceof VectorizedFilter vf) { + vectorizedFilter = vf; + } else if (filter instanceof Pattern p) { + vectorizedFilter = SimdSupport.vectorizedPattern(p); + } + if (vectorizedFilter != null) { + // also pass original? + return new MaskFilter.VectorizedMaskFilter(vectorizedFilter, this); + } + } return new MaskFilter<>(filter, this); }