Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vectorized map palette conversion #7

Merged
merged 2 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ def jvm_arguments = [

def compiler_jvm_arguments = [
'--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED',
'--add-exports=java.base/jdk.internal.reflect=ALL-UNNAMED'
'--add-exports=java.base/jdk.internal.reflect=ALL-UNNAMED',
'--add-modules=jdk.incubator.vector' // CatRoom - SIMD support
]
// Projects

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,19 @@
this.field_154332_n = new ServerEula(new File("eula.txt"));

if (!this.field_154332_n.func_154346_a())
@@ -163,17 +202,17 @@
@@ -152,6 +191,11 @@
this.func_71189_e(this.field_71340_o.func_73671_a("server-ip", ""));
}

+ // CatRoom start - SIMD support
+ try {
+ gg.pufferfish.pufferfish.simd.SIMDDetection.initialize();
+ } catch (Throwable ignored) {}
+ // CatRoom end - SIMD support
this.func_71251_e(this.field_71340_o.func_73670_a("spawn-animals", true));
this.func_71257_f(this.field_71340_o.func_73670_a("spawn-npcs", true));
this.func_71188_g(this.field_71340_o.func_73670_a("pvp", true));
@@ -163,17 +207,17 @@

if (this.field_71340_o.func_73669_a("difficulty", 1) < 0)
{
Expand All @@ -168,7 +180,7 @@
InetAddress inetaddress = null;

if (!this.func_71211_k().isEmpty())
@@ -186,29 +225,42 @@
@@ -186,29 +230,42 @@
this.func_71208_b(this.field_71340_o.func_73669_a("server-port", 25565));
}

Expand Down Expand Up @@ -227,7 +239,7 @@
field_155771_h.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
}

@@ -223,7 +275,9 @@
@@ -223,7 +280,9 @@
}
else
{
Expand All @@ -238,7 +250,7 @@
long j = System.nanoTime();

if (this.func_71270_I() == null)
@@ -234,7 +288,7 @@
@@ -234,7 +293,7 @@
String s = this.field_71340_o.func_73671_a("level-seed", "");
String s1 = this.field_71340_o.func_73671_a("level-type", "DEFAULT");
String s2 = this.field_71340_o.func_73671_a("generator-settings", "");
Expand All @@ -247,7 +259,7 @@

if (!s.isEmpty())
{
@@ -247,7 +301,7 @@
@@ -247,7 +306,7 @@
k = l;
}
}
Expand All @@ -256,7 +268,7 @@
{
k = (long)s.hashCode();
}
@@ -267,21 +321,21 @@
@@ -267,21 +326,21 @@
this.func_71191_d(this.field_71340_o.func_73669_a("max-build-height", 256));
this.func_71191_d((this.func_71207_Z() + 8) / 16 * 16);
this.func_71191_d(MathHelper.func_76125_a(this.func_71207_Z(), 64, 256));
Expand Down Expand Up @@ -285,7 +297,7 @@
this.field_71340_o.func_187238_b("announce-player-achievements");
this.field_71340_o.func_73668_b();
}
@@ -293,14 +347,33 @@
@@ -293,14 +352,33 @@
this.field_71342_m.func_72602_a();
}

Expand Down Expand Up @@ -324,7 +336,7 @@
{
Thread thread1 = new Thread(new ServerHangWatchdog(this));
thread1.setName("Server Watchdog");
@@ -309,7 +382,8 @@
@@ -309,7 +387,8 @@
}

Items.field_190931_a.func_150895_a(CreativeTabs.field_78027_g, NonNullList.func_191196_a());
Expand All @@ -334,7 +346,7 @@
}
}
}
@@ -339,46 +413,38 @@
@@ -339,46 +418,38 @@

if (!this.field_71340_o.func_73671_a("resource-pack", "").isEmpty() && s.isEmpty())
{
Expand Down Expand Up @@ -382,7 +394,7 @@
public CrashReport func_71230_b(CrashReport p_71230_1_)
{
p_71230_1_ = super.func_71230_b(p_71230_1_);
@@ -400,40 +466,34 @@
@@ -400,40 +471,34 @@
return p_71230_1_;
}

Expand Down Expand Up @@ -425,7 +437,7 @@
public boolean func_70002_Q()
{
return this.field_71340_o.func_73670_a("snooper-enabled", true);
@@ -446,20 +506,39 @@
@@ -446,20 +511,39 @@

public void func_71333_ah()
{
Expand Down Expand Up @@ -469,7 +481,7 @@
public boolean func_181035_ah()
{
return this.field_71340_o.func_73670_a("use-native-transport", true);
@@ -470,13 +549,11 @@
@@ -470,13 +554,11 @@
return (DedicatedPlayerList)super.func_184103_al();
}

Expand All @@ -483,7 +495,7 @@
public String func_71330_a(String p_71330_1_, String p_71330_2_)
{
return this.field_71340_o.func_73671_a(p_71330_1_, p_71330_2_);
@@ -487,38 +564,32 @@
@@ -487,38 +569,32 @@
return this.field_71340_o.func_73670_a(p_71332_1_, p_71332_2_);
}

Expand Down Expand Up @@ -522,7 +534,7 @@
public String func_71274_v()
{
return this.func_71273_Y();
@@ -530,34 +601,29 @@
@@ -530,34 +606,29 @@
this.field_71335_s = true;
}

Expand Down Expand Up @@ -558,7 +570,7 @@
{
return false;
}
@@ -583,33 +649,28 @@
@@ -583,33 +654,28 @@
}
}

Expand Down Expand Up @@ -593,7 +605,7 @@
public int func_175580_aG()
{
int i = this.field_71340_o.func_73669_a("max-world-size", super.func_175580_aG());
@@ -626,12 +687,14 @@
@@ -626,12 +692,14 @@
return i;
}

Expand All @@ -609,7 +621,7 @@
protected boolean func_152368_aE() throws IOException
{
boolean flag = false;
@@ -708,8 +771,9 @@
@@ -708,8 +776,9 @@
{
Thread.sleep(5000L);
}
Expand All @@ -620,7 +632,7 @@
}
}

@@ -718,17 +782,68 @@
@@ -718,17 +787,68 @@
return this.field_71340_o.func_179885_a("max-tick-time", TimeUnit.MINUTES.toMillis(1L));
}

Expand Down
45 changes: 45 additions & 0 deletions src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package gg.pufferfish.pufferfish.simd;

import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorSpecies;

/**
* Basically, java is annoying and we have to push this out to its own class.
*/
public class SIMDChecker {

public static void initialize() {
if (SIMDDetection.isInitialized()) {
return;
}
SIMDDetection.setInitialized();
try {
int javaVersion = SIMDDetection.getJavaVersion();
if (javaVersion < 17) {
return;
}
SIMDDetection.supportingJavaVersion = true;
SIMDDetection.testRunStarted = true;

VectorSpecies<Integer> ISPEC = IntVector.SPECIES_PREFERRED;
VectorSpecies<Float> FSPEC = FloatVector.SPECIES_PREFERRED;

SIMDDetection.intVectorBitSize = ISPEC.vectorBitSize();
SIMDDetection.floatVectorBitSize = FSPEC.vectorBitSize();

SIMDDetection.intElementSize = ISPEC.elementSize();
SIMDDetection.floatElementSize = FSPEC.elementSize();

SIMDDetection.testRunCompleted = true;

if (ISPEC.elementSize() < 2 || FSPEC.elementSize() < 2) {
SIMDDetection.unsupportingLaneSize = true;
return;
}

SIMDDetection.isEnabled = true;
} catch (Throwable ignored) {} // Basically, we don't do anything. This lets us detect if it's not functional and disable it.
}

}
75 changes: 75 additions & 0 deletions src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package gg.pufferfish.pufferfish.simd;

public class SIMDDetection {

private static boolean isInitialized = false;
static int intVectorBitSize;
static int floatVectorBitSize;
static int intElementSize;
static int floatElementSize;
static boolean supportingJavaVersion;
static boolean testRunStarted;
static boolean testRunCompleted;
static boolean unsupportingLaneSize;
static boolean isEnabled;

public static void initialize() {
try {
SIMDChecker.initialize();
} catch (Throwable ignored) {}
}

static void setInitialized() {
isInitialized = true;
}

public static boolean isInitialized() {
return isInitialized;
}

public static int intVectorBitSize() {
return intVectorBitSize;
}

public static int floatVectorBitSize() {
return floatVectorBitSize;
}

public static int intElementSize() {
return intElementSize;
}

public static int floatElementSize() {
return floatElementSize;
}

public static boolean supportingJavaVersion() {
return supportingJavaVersion;
}

public static boolean testRunCompleted() {
return testRunCompleted;
}

public static boolean unsupportingLaneSize() {
return unsupportingLaneSize;
}

public static boolean isEnabled() {
return isEnabled;
}

public static int getJavaVersion() {
// https://stackoverflow.com/a/2591122
String version = System.getProperty("java.version");
if(version.startsWith("1.")) {
version = version.substring(2, 3);
} else {
int dot = version.indexOf(".");
if(dot != -1) { version = version.substring(0, dot); }
}
version = version.split("-")[0]; // Azul is stupid
return Integer.parseInt(version);
}

}
82 changes: 82 additions & 0 deletions src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package gg.pufferfish.pufferfish.simd;

import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;
import org.bukkit.map.MapPalette;

import java.awt.*;

public class VectorMapPalette {

private static final VectorSpecies<Integer> I_SPEC = IntVector.SPECIES_PREFERRED;
private static final VectorSpecies<Float> F_SPEC = FloatVector.SPECIES_PREFERRED;

public static void matchColorVectorized(int[] in, byte[] out) {
int speciesLength = I_SPEC.length();
int i;
for (i = 0; i < in.length - speciesLength; i += speciesLength) {
float[] redsArr = new float[speciesLength];
float[] bluesArr = new float[speciesLength];
float[] greensArr = new float[speciesLength];
int[] alphasArr = new int[speciesLength];

for (int j = 0; j < speciesLength; j++) {
alphasArr[j] = (in[i + j] >> 24) & 0xFF;
redsArr[j] = (in[i + j] >> 16) & 0xFF;
greensArr[j] = (in[i + j] >> 8) & 0xFF;
bluesArr[j] = (in[i + j] >> 0) & 0xFF;
}

IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0);
FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0);
FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0);
FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0);
IntVector resultIndex = IntVector.zero(I_SPEC);
VectorMask<Integer> modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff);

modificationMask = modificationMask.and(alphas.lt(128).not());
FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE);

for (int c = 4; c < MapPalette.colors.length; c++) {
// We're using 32-bit floats here because it's 2x faster and nobody will know the difference.
// For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary.
FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed());
FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen());
FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue());

FloatVector rMean = reds.add(compReds).div(2.0f);
FloatVector rDiff = reds.sub(compReds);
FloatVector gDiff = greens.sub(compGreens);
FloatVector bDiff = blues.sub(compBlues);

FloatVector weightR = rMean.div(256.0f).add(2);
FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f);
FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f);

FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff));

// Now we compare to the best distance we've found.
// This mask contains a "1" if better, and a "0" otherwise.
VectorMask<Float> bestDistanceMask = distance.lt(bestDistances);
bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances

// Update the result array
// We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough.
resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results
}

for (int j = 0; j < speciesLength; j++) {
int index = resultIndex.lane(j);
out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127));
}
}

// For the final ones, fall back to the regular method
for (; i < in.length; i++) {
out[i] = MapPalette.matchColor(new Color(in[i], true));
}
}

}
Loading
Loading