From 794ba3e0425d4ea2b3e987a7b1004e574a011b15 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 16 Mar 2019 23:28:18 +0100 Subject: [PATCH] IT WORKS! IT WORKS FAST! Removed previous DirectRenderer Fixed map ID for renderers Moved everything related to the kernel to a new class in hopes of fixing the array being 0 length (apparently it was lambdas) Storing a reference of the pixels and converted colors at each renderer Implemented matchColor in a CL-compatible way Added and removed pixel array length check - it kept overindexing at first, but that got fixed eventually and the check crashed Aparapi in GPU mode Work of today --- .../sznp/virtualcomputer/DirectRenderer.java | 105 ----------------- .../src/sznp/virtualcomputer/GPURenderer.java | 53 ++++----- .../virtualcomputer/GPURendererInternal.java | 110 ++++++++++++++++++ .../src/sznp/virtualcomputer/IRenderer.java | 1 + .../sznp/virtualcomputer/MCFrameBuffer.java | 15 ++- .../src/sznp/virtualcomputer/PluginMain.java | 6 +- .../src/sznp/virtualcomputer/Test.java | 26 ++++- 7 files changed, 168 insertions(+), 148 deletions(-) delete mode 100644 VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java create mode 100644 VirtualComputer/src/sznp/virtualcomputer/GPURendererInternal.java diff --git a/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java b/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java deleted file mode 100644 index bb6d76a..0000000 --- a/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java +++ /dev/null @@ -1,105 +0,0 @@ -package sznp.virtualcomputer; - -import com.sun.jna.Pointer; -import net.minecraft.server.v1_12_R1.WorldMap; -import org.bukkit.World; -import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_12_R1.map.RenderData; -import org.bukkit.entity.Player; -import org.bukkit.map.MapCanvas; -import org.bukkit.map.MapRenderer; -import org.bukkit.map.MapView; - -import java.lang.reflect.Field; -import java.util.Map; - -public class DirectRenderer implements IRenderer { - private int startindex; - private byte[] buffer; - private MapView map; - - /** - * Attempt to use version-specific implementation - * - * @param id - * The ID of the current map - * @param world - * The world to create new maps in - * @param startindex - * The index to start from in allpixels - * @throws Exception - * Usually happens on incompatibility - */ - public DirectRenderer(short id, World world, int startindex) throws Exception, Exception, Exception, Exception { - map = IRenderer.prepare(id, world); - final Field field = map.getClass().getDeclaredField("renderCache"); - field.setAccessible(true); - @SuppressWarnings("unchecked") - final Map renderCache = (Map) field.get(map); - - RenderData render = renderCache.get(null); - - if (render == null) - renderCache.put(null, render = new RenderData()); - - this.startindex = startindex; - this.buffer = render.buffer; - map.addRenderer(new DummyRenderer()); - } - - private final class DummyRenderer extends MapRenderer { - @Override - public void render(MapView map, MapCanvas canvas, Player player) { - DirectRenderer.this.render(x, y, width, height); //Render after zeroing whole map - } - } - - private Exception ex; - private long x, y, width, height; - private long lastrender; - - @SuppressWarnings("deprecation") - public void render(long x, long y, long width, long height) { // TODO - //TODO: |CRASH| Prevent trying to read memory after computer is stopped - this.x=x; - this.y=y; - this.width=width; - this.height=height; - if(System.nanoTime()-lastrender<100*1000*1000) - return; - try { - //long p = PluginMain.pxc.updateAndGetMap((int) x, (int) y, (int) width, (int) height, null); - long p = 0; //TODO: Not used (class) - if (p == 0) return; - byte[] img = new Pointer(p).getByteArray(0, 128 * 128); - boolean hascolor=false; - for (int j = 0; j < buffer.length; j++) { - if (PluginMain.Instance.checkMachineNotRunning(null)) - return; - buffer[j] = img[j]; - if (img[j] != 0) - hascolor=true; - } - if(hascolor) - System.out.println("Some color!"); - else return; - final Field field = map.getClass().getDeclaredField("worldMap"); - field.setAccessible(true); - WorldMap wmap = (WorldMap) field.get(map); - wmap.flagDirty(0, 0); - wmap.flagDirty(127, 127); // Send the whole image - TODO: Only send changes - /* - * final Field fieldf = map.getClass().getDeclaredField("renderCache"); fieldf.setAccessible(true); - * @SuppressWarnings("unchecked") final Map renderCache = (Map) fieldf.get(map); RenderData render = renderCache.get(null); - * System.out.println("==: " + (buffer == render.buffer)); System.out.println("equals:" + Arrays.equals(buffer, render.buffer)); - */ - } catch (Exception e) { - if (ex != null && (e.getMessage() == ex.getMessage() //Checking for null with the == - || (e.getMessage() != null && e.getMessage().equals(ex.getMessage())))) - return; - (ex = e).printStackTrace(); - } finally { - lastrender = System.nanoTime(); //Even if there was an error, wait for the next render - } - } -} diff --git a/VirtualComputer/src/sznp/virtualcomputer/GPURenderer.java b/VirtualComputer/src/sznp/virtualcomputer/GPURenderer.java index fafe0c9..7c65f11 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/GPURenderer.java +++ b/VirtualComputer/src/sznp/virtualcomputer/GPURenderer.java @@ -1,33 +1,43 @@ package sznp.virtualcomputer; -import com.aparapi.Kernel; -import com.aparapi.Range; -import lombok.Setter; +import lombok.val; import net.minecraft.server.v1_12_R1.WorldMap; import org.bukkit.World; import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; import org.bukkit.craftbukkit.v1_12_R1.map.RenderData; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; +import org.bukkit.map.MapPalette; import org.bukkit.map.MapRenderer; import org.bukkit.map.MapView; +import java.awt.*; import java.lang.reflect.Field; import java.util.Map; public class GPURenderer extends MapRenderer implements IRenderer { private byte[] buffer; - private MapView map; - private Kernel kernel; - @Setter - private static int width; - private Range range; + private GPURendererInternal kernel; + //Store at central location after conversion + private static int[] colors_; public GPURenderer(short id, World world, int mapx, int mapy) throws Exception { - map = IRenderer.prepare(id, world); - final Field field = map.getClass().getDeclaredField("renderCache"); + MapView map = IRenderer.prepare(id, world); + if (map == null) return; //Testing + Field field = map.getClass().getDeclaredField("renderCache"); field.setAccessible(true); - @SuppressWarnings("unchecked") final Map renderCache = (Map) field.get(map); + @SuppressWarnings("unchecked") val renderCache = (Map) field.get(map); + + if (colors_ == null) { + field = MapPalette.class.getDeclaredField("colors"); + field.setAccessible(true); + Color[] cs = (Color[]) field.get(null); + colors_ = new int[cs.length]; + for (int i = 0; i < colors_.length; i++) { + colors_[i] = cs[i].getRGB(); //TODO: BGR or RGB? + } + } + kernel = new GPURendererInternal(mapx, mapy, colors_); RenderData render = renderCache.get(null); @@ -36,18 +46,7 @@ public class GPURenderer extends MapRenderer implements IRenderer { this.buffer = render.buffer; - kernel = new Kernel() { - @Override - public void run() { - int mx = getGlobalId(0); - int my = getGlobalId(1); - int imgx = mx + mapx * 128; - int imgy = my + mapy * 128; - int imgi = imgy * width + imgx; - buffer[my * 128 + mx] = matchColor(PluginMain.pixels[imgi]); - } - }; - range = Range.create2D(128, 128); + //System.setProperty("com.codegen.config.enable.NEW", "true"); map.addRenderer(this); } @@ -55,11 +54,11 @@ public class GPURenderer extends MapRenderer implements IRenderer { @Override public void render(MapView map, MapCanvas canvas, Player player) { try { - if (width == 0) return; //TODO: Stop rendering after computer is stopped + if (kernel.isRendered()) return; //TODO: Stop rendering after computer is stopped Field field = canvas.getClass().getDeclaredField("buffer"); field.setAccessible(true); buffer = (byte[]) field.get(canvas); - kernel.put(buffer).put(PluginMain.pixels).execute(range).get(buffer); + kernel.render(buffer); field = map.getClass().getDeclaredField("worldMap"); field.setAccessible(true); WorldMap wmap = (WorldMap) field.get(map); @@ -69,8 +68,4 @@ public class GPURenderer extends MapRenderer implements IRenderer { e.printStackTrace(); } } - - private byte matchColor(int bgra) { //TODO - return 48; - } } diff --git a/VirtualComputer/src/sznp/virtualcomputer/GPURendererInternal.java b/VirtualComputer/src/sznp/virtualcomputer/GPURendererInternal.java new file mode 100644 index 0000000..ab8f341 --- /dev/null +++ b/VirtualComputer/src/sznp/virtualcomputer/GPURendererInternal.java @@ -0,0 +1,110 @@ +package sznp.virtualcomputer; + +import com.aparapi.Kernel; +import com.aparapi.Range; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.function.Consumer; + +//Accessing the GPURenderer results in ArrayIndexOutOfBoundsExceptions - IT'S THE LAMBDAS +public class GPURendererInternal extends Kernel { + private int mapx; + private int mapy; + private int width; + private int height; + private byte[] pixels; + //Can't use static fields because it leads to incorrect CL code + private int[] colors; + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private byte[] buffer; //References the map buffer + private Range range; + @Getter + private boolean rendered = true; + private static ArrayList renderers = new ArrayList<>(); + + //public static byte[] test=new byte[1]; - LAMBDAS + public GPURendererInternal(int mapx, int mapy, int[] colors) { + this.mapx = mapx; + this.mapy = mapy; + this.colors = colors; + range = Range.create2D(128, 128); + renderers.add(this); + } + + @Override + public void run() { + //Single booleans not found (no such field error) + int mx = getGlobalId(0); + int my = getGlobalId(1); + int imgx = mx + mapx * 128; + int imgy = my + mapy * 128; + int imgi = (imgy * width + imgx) * 4; + if (imgx >= width || imgy >= height) { //Array length check --> Unhandled exception string + buffer[my * 128 + mx] = matchColor(0, 0, 0); + //buffer[my*128+mx]=10; - LAMBDAS + return; + } + buffer[my * 128 + mx] = matchColor(pixels[imgi] & 0xFF, pixels[imgi + 1] & 0xFF, pixels[imgi + 2] & 0xFF); //Byte.toUnsignedInt + //buffer[my*128+mx]=10; - LAMBDAS + //Unhandled exception string (used & 255, after using toUnsignedInt on the blue color) - Not that, see above + } + + //Modified version of MapPalette.matchColor + private byte matchColor(int b, int g, int r) { + int index = 0; + double best = -1; + + for (int i = 4; i < colors.length; i++) { + double distance = distance(b, g, r, colors[i]); + if (distance < best || best == -1) { + best = distance; + index = i; + } + } + + // Minecraft has 143 colors, some of which have negative byte representations + return (byte) (index < 128 ? index : -129 + (index - 127)); + } + + //Can't use getXY prefix because it treats it as a getter + private double distance(int b1, int g1, int r1, int c2) { + int red2 = color(c2, RED); + double rmean = (r1 + red2) / 2.0; + double r = r1 - red2; + double g = g1 - color(c2, GREEN); + int b = b1 - color(c2, BLUE); + double weightR = 2 + rmean / 256.0; + double weightG = 4.0; + double weightB = 2 + (255 - rmean) / 256.0; + return weightR * r * r + weightG * g * g + weightB * b * b; + } + + private static int color(int bgra, int oc) { + return (bgra >> (oc * 8)) & 0xFF; + } + + private static final int RED = 2; + private static final int GREEN = 1; + private static final int BLUE = 0; + + @SuppressWarnings("Convert2Lambda") //Aparapi fails with lambdas + public static void setPixels(byte[] pixels, int width, int height) { + renderers.forEach(new Consumer() { + @Override //IT'S THE LAMBDAS (exception) + public void accept(GPURendererInternal r) { + r.pixels = pixels; + r.width = width; + r.height = height; + r.rendered = false; + } + }); + } + + void render(byte[] buffer) { + if (pixels == null || rendered) return; + this.buffer = buffer; + put(buffer).put(pixels).execute(range).get(buffer); + rendered = true; + } +} diff --git a/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java b/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java index 6d31d53..1bf7ee2 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java +++ b/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java @@ -6,6 +6,7 @@ import org.bukkit.map.MapView; public interface IRenderer { static MapView prepare(short id, World world) { + if (world == null) return null; //Testing @SuppressWarnings("deprecation") MapView map = Bukkit.getMap(id); if (map == null) diff --git a/VirtualComputer/src/sznp/virtualcomputer/MCFrameBuffer.java b/VirtualComputer/src/sznp/virtualcomputer/MCFrameBuffer.java index 5396676..de0460f 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/MCFrameBuffer.java +++ b/VirtualComputer/src/sznp/virtualcomputer/MCFrameBuffer.java @@ -86,7 +86,9 @@ public class MCFrameBuffer implements IFramebuffer { } private BukkitTask tt; - private BukkitTask tttt; + private Pointer pointer; + private int width; + private int height; @Override public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) { @@ -107,8 +109,10 @@ public class MCFrameBuffer implements IFramebuffer { System.out.println("whbppbplpf: " + w[0] + " " + h[0] + " " + bpp[0] + " " + bpl[0] + " " + pf[0]); if (PluginMain.direct) { //PluginMain.pxc.setSource(ptr[0], (int)w[0], (int)h[0], PluginMain.MCX, PluginMain.MCY); - PluginMain.pixels = new Pointer(ptr[0]).getByteArray(0L, (int) (w[0] * h[0] * 4)); - GPURenderer.setWidth((int) w[0]); + pointer = new Pointer(ptr[0]); + this.width = (int) w[0]; + this.height = (int) h[0]; + GPURendererInternal.setPixels(pointer.getByteArray(0L, (int) (w[0] * h[0] * 4)), (int) w[0], (int) h[0]); } else { PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4); if (width * height > 640 * 480) @@ -125,14 +129,15 @@ public class MCFrameBuffer implements IFramebuffer { @Override public void notifyUpdate(long x, long y, long width, long height) { - if(tttt != null) - tttt.cancel(); //We are getting updates, but the pixel array isn't updated - VB reacts slowly + /*if(tttt != null) + tttt.cancel();*/ //We are getting updates, but the pixel array isn't updated - VB reacts slowly /*tttt = Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, () -> { for (IRenderer r : PluginMain.renderers) if (r instanceof DirectRenderer) ((DirectRenderer) r).render(PluginMain.allpixels, x, y, width, height); System.out.println("Update!"); - The render is done each tick }, 5);*/ + GPURendererInternal.setPixels(pointer.getByteArray(0L, (this.width * this.height * 4)), this.width, this.height); //TODO: Only copy changed part } @Override diff --git a/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java b/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java index c525b1a..6261e44 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java +++ b/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java @@ -39,10 +39,6 @@ public class PluginMain extends JavaPlugin { */ //public static PXCLib pxc; public static boolean direct; - /** - * Only used if {@link #direct} is true. - */ - public static byte[] pixels; // Fired when plugin is first enabled @Override @@ -79,7 +75,7 @@ public class PluginMain extends JavaPlugin { //throw new NoClassDefFoundError("Test error pls ignore"); for (short i = 0; i < 5; i++) for (short j = 0; j < 4; j++) - renderers.add(new GPURenderer(i, Bukkit.getWorlds().get(0), i, j)); + renderers.add(new GPURenderer((short) (j * 5 + i), Bukkit.getWorlds().get(0), i, j)); //pxc = LibraryLoader.create(PXCLib.class).search(getDataFolder().getAbsolutePath()).load("pxc"); direct=true; ccs.sendMessage("§bUsing Direct Renderer, all good"); diff --git a/VirtualComputer/src/sznp/virtualcomputer/Test.java b/VirtualComputer/src/sznp/virtualcomputer/Test.java index c9dcec2..b58232e 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/Test.java +++ b/VirtualComputer/src/sznp/virtualcomputer/Test.java @@ -14,6 +14,9 @@ import java.nio.ByteBuffer; import java.util.stream.IntStream; public class Test { + private int[] x = new int[]{10}; + + @SuppressWarnings("deprecation") public static void main(String[] args) { /*System.out.println(new File("").getAbsolutePath()); PXCLib pxc = LibraryLoader.create(PXCLib.class).search(new File("").getAbsolutePath()).load("pxc"); @@ -25,8 +28,18 @@ public class Test { //final int[] b={10,80,10,3,32,20,56,85,51,968,156,5894,10,60,52}; //final int[] a={5,6}; //final int[] b={10,80}; - int[] a= IntStream.range(0, 640*480).toArray(); - final int[] res=new int[a.length]; + int[] a = IntStream.range(0, 640 * 480).toArray(); + final int[] res = new int[a.length]; + //java.awt.Color tc=new java.awt.Color(0); + /*GPURenderer gr; + try { + gr=new GPURenderer((short) 0, null, 0, 0); + } catch (Exception e) { + e.printStackTrace(); + return; + }*/ + //gr.pixels=new byte[1]; + int[] x = new Test().x; Kernel kernel=new Kernel() { @Override public void run() { @@ -34,7 +47,12 @@ public class Test { //System.out.println(i); //res[i]=a[i]+b[i]; //res[i]= MapPalette.matchColor(a[i] & 0x0000FF, a[i] & 0x00FF00 >> 8, a[i] & 0xFF0000 >> 16); - res[i] = a[i]; + //res[i] = a[i]; + //res[i]=tc.getBlue(); + //gr.pixels[0]=5; + res[i] = x[0]; + //GPURenderer.px[0]=10; + //GPURendererInternal.test[0]=10; - LAMBDAS } }; long t = System.nanoTime(); @@ -50,7 +68,7 @@ public class Test { t=System.nanoTime(); for (int i = 0; i < res.length; i++) { - Color c=Color.fromBGR((int)a[i]); + Color c = Color.fromBGR(a[i]); res[i]= MapPalette.matchColor(c.getRed(), c.getGreen(), c.getBlue()); } System.out.println("For loop time: "+(System.nanoTime()-t)/1000000f+"ms");