From 2c7538a26a4e9ed34e1bab380a39b78722dd217a Mon Sep 17 00:00:00 2001 From: fridtjof Date: Fri, 31 Dec 2021 01:49:34 +0100 Subject: [PATCH 1/5] new platform/out: JNI on Linux! --- .gitignore | 5 ++ GNUmakefile | 1 + Makefiles/sledconf.unix_jni | 21 +++++ Makefiles/unix_jni.GNUmakefile | 34 +++++++ src/modules/out_jnibuf.c | 157 +++++++++++++++++++++++++++++++++ src/os/JniSled.java | 35 ++++++++ src/os/os_unix_jni.c | 31 +++++++ src/os/os_unix_jni.libs | 1 + 8 files changed, 285 insertions(+) create mode 100644 Makefiles/sledconf.unix_jni create mode 100644 Makefiles/unix_jni.GNUmakefile create mode 100644 src/modules/out_jnibuf.c create mode 100644 src/os/JniSled.java create mode 100644 src/os/os_unix_jni.c create mode 100644 src/os/os_unix_jni.libs diff --git a/.gitignore b/.gitignore index 750dc9e4..9755af8c 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,8 @@ sled.wasm.map # nix default result symlink (from nix-build) /result + +# JNI build files +src/os/JniSled.class +src/os/sh_tty_sled_JniSled.h +/sled.jar diff --git a/GNUmakefile b/GNUmakefile index 45a022df..18b4ff12 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -171,6 +171,7 @@ include Makefiles/card10.GNUmakefile include Makefiles/esp32.GNUmakefile include Makefiles/ndless.GNUmakefile include Makefiles/emscripten.GNUmakefile +include Makefiles/unix_jni.GNUmakefile # --- All/Cleaning begins here --- diff --git a/Makefiles/sledconf.unix_jni b/Makefiles/sledconf.unix_jni new file mode 100644 index 00000000..a49a1c2f --- /dev/null +++ b/Makefiles/sledconf.unix_jni @@ -0,0 +1,21 @@ +# Example sledconf for building sled as a JNI library + +# Build with `make sled.jar` + +PROJECT := sled + +DEBUG := 0 + +# I've only validated it to work in static form. Dynamic will need extra handling/work in unix_jni.GNUmakefile +STATIC := 1 +PLATFORM := unix_jni + +# Avoid touching CFLAGS too much. + +DEFAULT_OUTMOD := jnibuf +DEFAULT_MODULEDIR := "./modules" +MODULES := $(MODULES_DEFAULT) out_$(DEFAULT_OUTMOD) + +# TODO these are useless now. Set them in src/os/JniSled.java instead. +#MATRIX_X := 256 +#MATRIX_Y := 256 diff --git a/Makefiles/unix_jni.GNUmakefile b/Makefiles/unix_jni.GNUmakefile new file mode 100644 index 00000000..b144ce69 --- /dev/null +++ b/Makefiles/unix_jni.GNUmakefile @@ -0,0 +1,34 @@ +ifeq ($(PLATFORM),unix_jni) + +ifeq (,${JAVA_HOME}) + #$(error Please set JAVA_HOME) + JAVA_HOME := /usr/lib/jvm/java-17-openjdk +endif + +STATIC := 1 + +CFLAGS += -fPIC + +CFLAGS += -I${JAVA_HOME}/include/ +CFLAGS += -I${JAVA_HOME}/include/linux + + +src/os/sh_tty_sled_JniSled.h src/os/JniSled.class: src/os/JniSled.java + javac -h src/os $^ + +sh/tty/sled/JniSled.class: src/os/JniSled.class + mkdir -p $(shell dirname $@) + cp $^ $@ + +JNI_PROJECT := sled + +#OBJECTS += sh_tty_sled_JniSled.h + +#PROJECT := $(JNI_PROJECT).jar + +lib$(PROJECT).so: $(OBJECTS) $(ML_OBJECTS) + $(CC) -shared $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(shell cat $(PLATFORM_LIBS) $(MODULES_STATIC_LIBS) 2>/dev/null || true) + +$(PROJECT).jar: lib$(PROJECT).so sh/tty/sled/JniSled.class + zip $@ $^ +endif diff --git a/src/modules/out_jnibuf.c b/src/modules/out_jnibuf.c new file mode 100644 index 00000000..585b1cbc --- /dev/null +++ b/src/modules/out_jnibuf.c @@ -0,0 +1,157 @@ +// Dummy output. +// +// Copyright (c) 2021, fridtjof +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include "../os/sh_tty_sled_JniSled.h" + +#include +#include +#include +#include +#include +#include "stdio.h" + +#define PPOS(x, y) (x + (y * matx)) + +static size_t JNI_BUFFER_SIZE; +static jintArray buffers[2]; +static jint* working_buffer; +static jint* currentBufferPtr; +static int currentBuffer = -1; +#define NEXTBUFFER ((currentBuffer + 1) % 2) + +extern JNIEnv *pJniEnv; +#define JniEnv (*pJniEnv) +extern jobject jo_jni_sled; + +static jclass jc_jni_sled; +static jmethodID jm_setCurrentBuffer; +static jfieldID jf_matx; +static jfieldID jf_maty; + +static int matx; +static int maty; + +void ignoreException() { + if (JniEnv->ExceptionCheck(pJniEnv)) { + // this doesn't actually output anything?? + JniEnv->ExceptionDescribe(pJniEnv); + JniEnv->ExceptionClear(pJniEnv); + } +} + +void swapBuffers() { + currentBuffer = NEXTBUFFER; + JniEnv->CallVoidMethodA(pJniEnv, jo_jni_sled, jm_setCurrentBuffer, (jvalue *) &buffers[currentBuffer]); + + // warnings constantly show up with -Xcheck:jni, I have no idea why + ignoreException(); +} + +inline unsigned int rgb2uint(RGB color) { + return (color.blue << 0) | (color.green << 8) | (color.red << 16) | (color.alpha << 24); +} + +inline RGB uint2rgb(unsigned int color) { + RGB col; + col.blue = (color << 0) & 0xFF; + col.green = (color << 8) & 0xFF; + col.red = (color << 16) & 0xFF; + col.alpha = (color << 24) & 0xFF; + + return col; +} + +int init(void) { + jc_jni_sled = JniEnv->GetObjectClass(pJniEnv, jo_jni_sled); + + // determine sizes + jf_matx = JniEnv->GetStaticFieldID(pJniEnv, jc_jni_sled, "MATRIX_X", "I"); + jf_maty = JniEnv->GetStaticFieldID(pJniEnv, jc_jni_sled, "MATRIX_Y", "I"); + matx = JniEnv->GetStaticIntField(pJniEnv, jc_jni_sled, jf_matx); + maty = JniEnv->GetStaticIntField(pJniEnv, jc_jni_sled, jf_maty); + JNI_BUFFER_SIZE = matx * maty * sizeof(jint); + + // allocate output buffers + working_buffer = malloc(JNI_BUFFER_SIZE); + buffers[0] = JniEnv->NewIntArray(pJniEnv, matx * maty); + buffers[1] = JniEnv->NewIntArray(pJniEnv, matx * maty); + + // get ready for rendering + jm_setCurrentBuffer = JniEnv->GetMethodID(pJniEnv, jc_jni_sled, "setCurrentBuffer", "([I)V"); + swapBuffers(); + + return 0; +} + +int getx(int _modno) { + return matx; +} +int gety(int _modno) { + return maty; +} + +inline void bounds_check(int x, int y) { + assert(x >= 0); + assert(y >= 0); + assert(x < matx); + assert(y < maty); +} + +int set(int _modno, int x, int y, RGB color) { + bounds_check(x, y); + working_buffer[PPOS(x, y)] = rgb2uint(color); + return 0; +} + +RGB get(int _modno, int x, int y) { + bounds_check(x, y); + return uint2rgb((unsigned int) working_buffer[PPOS(x, y)]); +} + +int clear(int _modno) { + memset(working_buffer, 0, JNI_BUFFER_SIZE); + return 0; +} + +int render(void) { + // prepare for working on the next frame + currentBufferPtr = JniEnv->GetIntArrayElements(pJniEnv, buffers[NEXTBUFFER], 0 /*ptr bool*/); + memcpy(currentBufferPtr, working_buffer, JNI_BUFFER_SIZE); + JniEnv->ReleaseIntArrayElements(pJniEnv, buffers[NEXTBUFFER], currentBufferPtr, 0); + swapBuffers(); + // notify? prob not needed i think? + return 0; +} + +oscore_time wait_until(int _modno, oscore_time desired_usec) { + // Hey, we can just delegate work to someone else. Yay! +#ifdef CIMODE + return desired_usec; +#else + return timers_wait_until_core(desired_usec); +#endif +} + +void wait_until_break(int _modno) { +#ifndef CIMODE + timers_wait_until_break_core(); +#endif +} + +void deinit(int _modno) { + // TODO actually do stuff here. Sled and/or jvm won't exit properly. + // maybe this needs some more fiddling in the os_ module too? +} diff --git a/src/os/JniSled.java b/src/os/JniSled.java new file mode 100644 index 00000000..b2e1197f --- /dev/null +++ b/src/os/JniSled.java @@ -0,0 +1,35 @@ +package sh.tty.sled; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; + +public class JniSled implements Runnable { + static { + System.loadLibrary("sled"); + } + + // only set these before starting the thread!!! + public static int MATRIX_X = 256; + public static int MATRIX_Y = 256; + + private int[] currentBuf; + + public native void main(); + + @Override + public void run() { + main(); + } + + // TODO: maybe make this trigger a callback optionally? + // could use that to let the user immediately render, + // "pushing" instead of polling + // this would let us have a variable frame rate dictated by sled. + public void setCurrentBuffer(int[] buf) { + currentBuf = buf; + } + + public int[] getCurrentBuffer() { + return currentBuf; + } +} \ No newline at end of file diff --git a/src/os/os_unix_jni.c b/src/os/os_unix_jni.c new file mode 100644 index 00000000..1614aa0c --- /dev/null +++ b/src/os/os_unix_jni.c @@ -0,0 +1,31 @@ +// os_unix_jni +// you're going to hate this +// +// Copyright (c) 2021, fridtjof +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#include "os_unix.c" + +#include "sh_tty_sled_JniSled.h" + +JNIEnv *pJniEnv; +jobject jo_jni_sled; + +JNIEXPORT void JNICALL Java_sh_tty_sled_JniSled_main(JNIEnv * env, jobject obj) { + pJniEnv = env; + jo_jni_sled = obj; + + char * args[] = {"sled"}; + sled_main(1,args); +} \ No newline at end of file diff --git a/src/os/os_unix_jni.libs b/src/os/os_unix_jni.libs new file mode 100644 index 00000000..1d2c98f4 --- /dev/null +++ b/src/os/os_unix_jni.libs @@ -0,0 +1 @@ +-lpthread From d8f91452cee87586fddb4af6e9d49e0bd5e831f9 Mon Sep 17 00:00:00 2001 From: fridtjof Date: Fri, 18 Feb 2022 21:48:04 +0100 Subject: [PATCH 2/5] jni: initialize x/y through the constructor instead of statically --- src/modules/out_jnibuf.c | 8 ++++---- src/os/JniSled.java | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/modules/out_jnibuf.c b/src/modules/out_jnibuf.c index 585b1cbc..d39e7828 100644 --- a/src/modules/out_jnibuf.c +++ b/src/modules/out_jnibuf.c @@ -78,10 +78,10 @@ int init(void) { jc_jni_sled = JniEnv->GetObjectClass(pJniEnv, jo_jni_sled); // determine sizes - jf_matx = JniEnv->GetStaticFieldID(pJniEnv, jc_jni_sled, "MATRIX_X", "I"); - jf_maty = JniEnv->GetStaticFieldID(pJniEnv, jc_jni_sled, "MATRIX_Y", "I"); - matx = JniEnv->GetStaticIntField(pJniEnv, jc_jni_sled, jf_matx); - maty = JniEnv->GetStaticIntField(pJniEnv, jc_jni_sled, jf_maty); + jf_matx = JniEnv->GetFieldID(pJniEnv, jc_jni_sled, "matrixX", "I"); + jf_maty = JniEnv->GetFieldID(pJniEnv, jc_jni_sled, "matrixY", "I"); + matx = JniEnv->GetIntField(pJniEnv, jo_jni_sled, jf_matx); + maty = JniEnv->GetIntField(pJniEnv, jo_jni_sled, jf_maty); JNI_BUFFER_SIZE = matx * maty * sizeof(jint); // allocate output buffers diff --git a/src/os/JniSled.java b/src/os/JniSled.java index b2e1197f..7234eff7 100644 --- a/src/os/JniSled.java +++ b/src/os/JniSled.java @@ -8,12 +8,16 @@ public class JniSled implements Runnable { System.loadLibrary("sled"); } - // only set these before starting the thread!!! - public static int MATRIX_X = 256; - public static int MATRIX_Y = 256; + private int matrixX = 256; + private int matrixY = 256; private int[] currentBuf; + public JniSled(int x, int y) { + matrixX = x; + matrixY = y; + } + public native void main(); @Override @@ -21,6 +25,14 @@ public void run() { main(); } + public int getMatrixX() { + return matrixX; + } + + public int getMatrixY() { + return matrixY; + } + // TODO: maybe make this trigger a callback optionally? // could use that to let the user immediately render, // "pushing" instead of polling From 3b79f94f042c8a5532c07984723f40bd8dac255f Mon Sep 17 00:00:00 2001 From: fridtjof Date: Fri, 18 Feb 2022 21:50:07 +0100 Subject: [PATCH 3/5] jni: rename a function to clarify what it does --- src/modules/out_jnibuf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/out_jnibuf.c b/src/modules/out_jnibuf.c index d39e7828..c3d842fe 100644 --- a/src/modules/out_jnibuf.c +++ b/src/modules/out_jnibuf.c @@ -44,7 +44,7 @@ static jfieldID jf_maty; static int matx; static int maty; -void ignoreException() { +void print_any_exceptions() { if (JniEnv->ExceptionCheck(pJniEnv)) { // this doesn't actually output anything?? JniEnv->ExceptionDescribe(pJniEnv); @@ -56,8 +56,7 @@ void swapBuffers() { currentBuffer = NEXTBUFFER; JniEnv->CallVoidMethodA(pJniEnv, jo_jni_sled, jm_setCurrentBuffer, (jvalue *) &buffers[currentBuffer]); - // warnings constantly show up with -Xcheck:jni, I have no idea why - ignoreException(); + print_any_exceptions(); } inline unsigned int rgb2uint(RGB color) { From 0c88a1961c82637a1899ac38087cef0b68162eed Mon Sep 17 00:00:00 2001 From: fridtjof Date: Fri, 18 Feb 2022 21:52:59 +0100 Subject: [PATCH 4/5] jni: hide an implementation detail from users --- src/os/JniSled.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/os/JniSled.java b/src/os/JniSled.java index 7234eff7..830054f6 100644 --- a/src/os/JniSled.java +++ b/src/os/JniSled.java @@ -26,22 +26,23 @@ public void run() { } public int getMatrixX() { - return matrixX; + return matrixX; } public int getMatrixY() { return matrixY; } + public int[] getCurrentBuffer() { + return currentBuf; + } + + // called from out_jnibuf // TODO: maybe make this trigger a callback optionally? // could use that to let the user immediately render, // "pushing" instead of polling // this would let us have a variable frame rate dictated by sled. - public void setCurrentBuffer(int[] buf) { + private void setCurrentBuffer(int[] buf) { currentBuf = buf; } - - public int[] getCurrentBuffer() { - return currentBuf; - } } \ No newline at end of file From aa9550fb3092005d5042399bc86556b9710d5a41 Mon Sep 17 00:00:00 2001 From: fridtjof Date: Fri, 18 Feb 2022 21:53:46 +0100 Subject: [PATCH 5/5] out_jnibuf: address a compiler warning --- src/modules/out_jnibuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/out_jnibuf.c b/src/modules/out_jnibuf.c index c3d842fe..e5877905 100644 --- a/src/modules/out_jnibuf.c +++ b/src/modules/out_jnibuf.c @@ -102,7 +102,7 @@ int gety(int _modno) { return maty; } -inline void bounds_check(int x, int y) { +static inline void bounds_check(int x, int y) { assert(x >= 0); assert(y >= 0); assert(x < matx);