Add a gui demo
authorNot Zed <notzed@gmail.com>
Wed, 8 Dec 2021 00:23:33 +0000 (10:53 +1030)
committerNot Zed <notzed@gmail.com>
Wed, 8 Dec 2021 00:23:33 +0000 (10:53 +1030)
Makefile
src/notzed.zcl.demo.fx/classes/fxdemo/fract/Calculate.java [new file with mode: 0644]
src/notzed.zcl.demo.fx/classes/fxdemo/fract/Mandelbrot.java [new file with mode: 0644]
src/notzed.zcl.demo.fx/classes/fxdemo/fract/mandelbrot.cl [new file with mode: 0644]
src/notzed.zcl.demo.fx/classes/module-info.java [new file with mode: 0644]

index bcc5f51..b2ba201 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,18 @@ dist_EXTRA=README                            \
 
 include config.make
 
-java_MODULES=notzed.zcl notzed.zcl.demo
+java_MODULES=notzed.zcl notzed.zcl.demo notzed.zcl.demo.fx
 notzed.zcl.demo_JDEPMOD=notzed.zcl
+notzed.zcl.demo.fx_JDEPMOD=notzed.zcl
 
 include java.make
+
+mandelbrot: all
+       LD_LIBRARY_PATH=../nativez/bin/notzed.nativez/linux-amd64/lib:bin/notzed.zcl/linux-amd64/lib \
+       $(JAVA) --module-path bin/modules:../nativez/bin/modules:$(JAVAFX_HOME)/lib \
+               -m notzed.zcl.demo.fx/fxdemo.fract.Mandelbrot $(ARGS)
+
+clinfo: all
+       LD_LIBRARY_PATH=../nativez/bin/notzed.nativez/linux-amd64/lib:bin/notzed.zcl/linux-amd64/lib \
+       strace -e open -o log $(JAVA) --module-path bin/modules:../nativez/bin/modules \
+               -m notzed.zcl.demo/au.notzed.zcl.tools.clinfo $(ARGS)
diff --git a/src/notzed.zcl.demo.fx/classes/fxdemo/fract/Calculate.java b/src/notzed.zcl.demo.fx/classes/fxdemo/fract/Calculate.java
new file mode 100644 (file)
index 0000000..b1ed8fd
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package fxdemo.fract;
+
+import static au.notzed.zcl.CL.*;
+import au.notzed.zcl.CLBuffer;
+import au.notzed.zcl.CLCommandQueue;
+import au.notzed.zcl.CLContext;
+import au.notzed.zcl.CLDevice;
+import au.notzed.zcl.CLException;
+import au.notzed.zcl.CLKernel;
+import au.notzed.zcl.CLPlatform;
+import au.notzed.zcl.CLProgram;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import java.nio.ByteBuffer;
+
+public class Calculate {
+
+       CLDevice[] devs;
+       CLContext cl;
+       CLCommandQueue q;
+       CLBuffer image;
+       CLProgram prog;
+       int width, height;
+       CLKernel mandelbrot;
+       CLKernel dmandelbrot;
+
+       Calculate(int width, int height) throws CLException {
+               devs = new CLDevice[]{CLPlatform.getBestDevice(CL_DEVICE_TYPE_ALL)};
+               cl = CLContext.createContext(null, devs);
+               q = cl.createCommandQueue(devs[0], 0);
+
+               this.width = width;
+               this.height = height;
+
+               image = cl.createBuffer(0, width * height * 4);
+
+               prog = cl.createProgramWithSource(getClass().getResourceAsStream("mandelbrot.cl"));
+               prog.buildProgram(devs, null);
+
+               mandelbrot = prog.createKernel("mandelbrot");
+               mandelbrot.setArg(0, image);
+               mandelbrot.setArg(1, width, height);
+
+               dmandelbrot = prog.createKernel("dmandelbrot");
+               dmandelbrot.setArg(0, image);
+               dmandelbrot.setArg(1, width, height);
+       }
+
+       public void run(ByteBuffer buffer, double ox, double oy, double sx, double sy, double arg, int m) {
+               try {
+                       mandelbrot.setArg(2, ox, oy);
+                       mandelbrot.setArg(3, sx, sy);
+                       mandelbrot.setArg(4, sin(arg), cos(arg));
+                       mandelbrot.setArg(5, m);
+                       System.out.println("enqueue");
+                       q.enqueue2DKernel(mandelbrot, 0, 0, width, height, 8, 8, null, null);
+                       q.enqueueReadBuffer(image, true, 0, buffer.limit(), buffer, null, null);
+                       System.out.println("finish");
+                       q.finish();
+               } catch (CLException ex) {
+                       ex.printStackTrace();
+               }
+       }
+
+       public void rund(ByteBuffer buffer, double ox, double oy, double sx, double sy, double arg, int m) {
+               try {
+                       dmandelbrot.setArg(2, ox, oy);
+                       dmandelbrot.setArg(3, sx, sy);
+                       dmandelbrot.setArg(4, sin(arg), cos(arg));
+                       dmandelbrot.setArg(5, m);
+
+                       System.out.println("enqueue");
+                       q.enqueue2DKernel(dmandelbrot, 0, 0, width, height, 8, 8, null, null);
+                       q.enqueueReadBuffer(image, true, 0, buffer.limit(), buffer, null, null);
+                       System.out.println("finish");
+                       q.finish();
+               } catch (CLException ex) {
+                       ex.printStackTrace();
+               }
+
+       }
+
+}
diff --git a/src/notzed.zcl.demo.fx/classes/fxdemo/fract/Mandelbrot.java b/src/notzed.zcl.demo.fx/classes/fxdemo/fract/Mandelbrot.java
new file mode 100644 (file)
index 0000000..12188a1
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package fxdemo.fract;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import javafx.animation.Interpolator;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.animation.Transition;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.event.ActionEvent;
+import javafx.scene.Scene;
+import javafx.scene.image.ImageView;
+import javafx.scene.image.PixelFormat;
+import javafx.scene.image.WritableImage;
+import javafx.scene.input.KeyCombination;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import java.nio.ByteBuffer;
+import au.notzed.zcl.CLMemory;
+
+/**
+ * Simple mandelbrot demo.
+ */
+public class Mandelbrot extends Application {
+
+       int width = 1920 / 2;
+       int height = 1200 / 2;
+       Calculate calc;
+       WritableImage image;
+       ByteBuffer buffer;
+       ExecutorService queue = Executors.newSingleThreadExecutor((r) -> {
+               Thread t = new Thread(r);
+               t.setDaemon(true);
+               return t;
+       });
+       float scale = 0.001f;
+       //double cx = -0.235125, cy = 0.827215;
+       //double cx = -0.74529, cy = 0.11307;
+       //      double cx = -0.761574, cy =-0.0847596;
+       double cx = -0.7746806106269039, cy = -0.1374168856037867;
+
+       long nframes;
+       long lframes;
+       boolean goin = true;
+       double vscale = 0.01;
+
+       @Override
+       public void start(Stage stage) throws Exception {
+
+               calc = new Calculate(width, height);
+               image = new WritableImage(width, height);
+
+               ImageView iv = new ImageView(image);
+               Text fps = new Text("FPS?");
+               fps.setStyle("-fx-font: 24 monospace; -fx-fill: orange; -fx-effect: dropshadow(two-pass-box, orangered, 6, 0.5, 0, 0);");
+
+               Scene scene = new Scene(new AnchorPane(iv, fps));
+
+               scene.getAccelerators().put(KeyCombination.valueOf("ESC"), stage::close);
+
+               //      stage.setFullScreen(true);
+               AnchorPane.setLeftAnchor(fps, 24.0);
+               AnchorPane.setTopAnchor(fps, 24.0);
+
+               stage.setScene(scene);
+               stage.show();
+
+               buffer = CLMemory.alloc(width * height * 4);
+
+               Timeline tofps = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {
+                       fps.setText(String.valueOf((nframes - lframes)));
+                       lframes = nframes;
+               }));
+               tofps.setCycleCount(Timeline.INDEFINITE);
+               tofps.play();
+
+               Transition zoom = new Transition(60) {
+                       {
+                               setCycleDuration(Duration.seconds(15));
+                       }
+
+                       @Override
+                       protected void interpolate(double frac) {
+                               //double scale = frac * (0.001 - 0.000001) + 0.000001;
+                               if (true) {
+                                       double scale = 0.005 / Math.exp(frac * 30);
+                                       recalcd(scale, (int) (-Math.log(scale) * 80));
+                               } else {
+                                       double scale = 0.005 / Math.exp(frac * 12);
+                                       recalc(scale, (int) (-Math.log(scale) * 160));
+                               }
+
+                               //System.out.printf("scale %e iter %f\n", scale, -Math.log(scale) * 100);
+                               //recalc((float) scale);
+                               //recalc(scale);
+                               //recalc(vscale);
+                               //vscale *= 0.95;
+                       }
+               };
+               zoom.setInterpolator(Interpolator.EASE_BOTH);
+               zoom.setAutoReverse(true);
+               zoom.setCycleCount(Timeline.INDEFINITE);
+               zoom.play();
+
+               //recalc();
+       }
+
+       boolean busy;
+       double arg = 0;
+
+       void recalcd(double scale, int m) {
+               arg -= 0.05;
+               if (true) {
+                       if (busy)
+                               return;
+                       busy = true;
+                       //System.out.println(scale);
+                       queue.submit(() -> {
+                               double x = cx - width * scale * 0.5;
+                               double y = cy - height * scale * 0.5;
+
+                               calc.rund(buffer, x, y, scale, scale, arg, m);
+                               Platform.runLater(() -> {
+                                       nframes++;
+                                       image.getPixelWriter()
+                                               .setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), buffer, width * 4);
+                                               busy = false;
+                               });
+                       });
+               } else {
+                       double x = cx - width * scale * 0.5;
+                       double y = cy - height * scale * 0.5;
+
+                       calc.rund(buffer, x, y, scale, scale, arg, m);
+                       nframes++;
+                       image.getPixelWriter()
+                               .setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), buffer, width * 4);
+               }
+       }
+
+       void recalc(double scale, int m) {
+               arg += 0.05;
+               if (true) {
+                       if (busy)
+                               return;
+                       busy = true;
+                       queue.submit(() -> {
+                               double x = cx - width * scale * 0.5;
+                               double y = cy - height * scale * 0.5;
+
+                               calc.run(buffer, x, y, scale, scale, arg, m);
+
+                               Platform.runLater(() -> {
+                                       nframes++;
+                                       image.getPixelWriter()
+                                               .setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), buffer, width * 4);
+                                       busy = false;
+                               });
+                       });
+               } else {
+                       double x = cx - width * scale * 0.5;
+                       double y = cy - height * scale * 0.5;
+
+                       calc.run(buffer, x, y, scale, scale, arg, m);
+                       nframes++;
+                       image.getPixelWriter()
+                               .setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), buffer, width * 4);
+               }
+
+       }
+
+}
diff --git a/src/notzed.zcl.demo.fx/classes/fxdemo/fract/mandelbrot.cl b/src/notzed.zcl.demo.fx/classes/fxdemo/fract/mandelbrot.cl
new file mode 100644 (file)
index 0000000..bb7009c
--- /dev/null
@@ -0,0 +1,97 @@
+
+#pragma OPENCL EXTENSION cl_khr_fp64 : enable
+
+#define LWS_X 8
+#define LWS_Y 8
+
+kernel void
+__attribute__((reqd_work_group_size(LWS_X, LWS_Y, 1)))
+mandelbrot(global uchar4 *image, int2 size, double2 origin, double2 scale, double2 sincos, int m) {
+       int dx = get_global_id(0);
+       int dy = get_global_id(1);
+
+       if (dx < size.x && dy < size.y) {
+#if 0
+               float2 c = (float2)(dx, dy) * scale + origin;
+#else
+               double2 s = convert_double2(size);
+               double2 v;
+
+               v = (double2)(dx, dy) - s * 0.5;
+               v = (double2)(v.x * sincos.x + v.y * sincos.y,
+                            v.x * sincos.y - v.y * sincos.x);
+               v += s * 0.5;
+
+               v *= scale;
+               v += origin;
+
+               float2 c = convert_float2(v);
+#endif
+               int n = 0;
+               float2 z = 0;
+
+               do {
+                       z = (float2)(z.x * z.x - z.y * z.y, 2.0f * z.x * z.y) + c;
+                       n++;
+               } while (dot(z, z) < 4 && n < m);
+
+               // we use a simple cosine palette to determine color:
+               // http://iquilezles.org/www/articles/palettes/palettes.htm
+               float t = n * 10.0f / m;
+               float3 d = (float3)(0.5f, 0.5f, 0.5f);
+               float3 e = (float3)(0.5f, 0.5f, 0.5f);
+               float3 f = (float3)(1.0f, 1.0f, 1.0f);
+               float3 g = (float3)(0.00f, 0.33f, 0.67f);
+               float4 colour = (float4)( d + e * cos(6.28318f * (f * t + g)), 1.0f);
+
+               if (convert_int(n) == m)
+                       colour = (float4)(0, 0, 0, 1);
+
+               // store the rendered mandelbrot set into a storage buffer:
+               image[dx + dy * size.x] = convert_uchar4(colour * 255.0f);
+       }
+}
+
+// rot-zoomer
+kernel void
+__attribute__((reqd_work_group_size(LWS_X, LWS_Y, 1)))
+dmandelbrot(global uchar4 *image, int2 size, double2 origin, double2 scale, double2 sincos, int m) {
+       int dx = get_global_id(0);
+       int dy = get_global_id(1);
+
+       if (dx < size.x && dy < size.y) {
+               double2 s = convert_double2(size);
+               double2 c;
+
+               c = (double2)(dx, dy) - s * 0.5;
+               c = (double2)(c.x * sincos.x + c.y * sincos.y,
+                             c.x * sincos.y - c.y * sincos.x);
+               c += s * 0.5;
+
+               c *= scale;
+               c += origin;
+
+               int n = 0;
+               double2 z = 0;
+
+               do {
+                       z = (double2)(z.x * z.x - z.y * z.y, 2.0f * z.x * z.y) + c;
+                       n++;
+               } while (dot(z, z) < 4 && n < m);
+
+               // we use a simple cosine palette to determine color:
+               // http://iquilezles.org/www/articles/palettes/palettes.htm
+               float t = n * 10.0f / m;
+               float3 d = (float3)(0.5f, 0.5f, 0.5f);
+               float3 e = (float3)(0.5f, 0.5f, 0.5f);
+               float3 f = (float3)(1.0f, 1.0f, 1.0f);
+               float3 g = (float3)(0.00f, 0.33f, 0.67f);
+               float4 colour = (float4)( d + e * cos(6.28318f * (f * t + g)), 1.0f);
+
+               if (convert_int(n) == m)
+                       colour = (float4)(0, 0, 0, 1);
+
+               // store the rendered mandelbrot set into a storage buffer:
+               image[dx + dy * size.x] = convert_uchar4(colour * 255.0f);
+       }
+}
diff --git a/src/notzed.zcl.demo.fx/classes/module-info.java b/src/notzed.zcl.demo.fx/classes/module-info.java
new file mode 100644 (file)
index 0000000..f9f8e8b
--- /dev/null
@@ -0,0 +1,15 @@
+
+/**
+ * OpenCL demos that use JavaFX.
+ */
+module notzed.zcl.demo.fx {
+       requires javafx.base;
+       requires javafx.controls;
+       requires javafx.graphics;
+       requires javafx.swing;
+
+       requires notzed.zcl;
+
+       exports fxdemo.fract to javafx.graphics;
+
+}