This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-06-26
Channels
- # babashka (63)
- # beginners (27)
- # calva (17)
- # cider (1)
- # clojure (23)
- # clojure-europe (6)
- # clojure-norway (4)
- # clojurescript (9)
- # cursive (8)
- # data-oriented-programming (9)
- # data-science (7)
- # fulcro (14)
- # graalvm (3)
- # helix (3)
- # introduce-yourself (1)
- # jobs-discuss (7)
- # membrane (40)
- # missionary (4)
- # off-topic (32)
- # pathom (60)
- # react (6)
- # releases (2)
- # shadow-cljs (4)
Hey there,
How can I start writing a membrane backend?
I tried copying skia.clj into my clojure project, and forking it in a git submodule. Either way, it can't get a working classpath. Missing membraneskia.so
and com.phronemophobic.membrane.Skia
.
Tried running clj -M:deploy
in the submodule, thinking it might build Skia
, but it's missing a pom.xml
.
writing a membrane backend isn't really documented, but I can try to help throw together some resources
what kind of backend are you trying to make?
as a template, I would start with the membrane.java2d
namespace since it doesn't have any external dependencies
Thanks, good idea. I just got a local java2d fork working. Hoping to experiment with a game loop.
Not sure if I need a game loop. I like the idea of writing an app that could work on any backend. But I'm not sure how to get an animation to run while no input events are happening. Maybe just changing to (glfw-call void glfwWaitEventsTimeout (double 1/60))
would be enough.
I wouldn't mix and match functions between membrane.skia
and membrane.java2d
. When you run
a ui with membrane.java2d/run
, it returns a map of window info that includes a repaint function and the jframe window. (see https://github.com/phronmophobic/membrane/blob/master/src/membrane/java2d.clj#L946)
In general, I probably wouldn't recommend calling any of the internal membrane.skia
functions unless you're familiar with skia and native programming. The native calls in membrane.skia
are a little trickier to use since incorrect usage can cause memory corruption or hard crash the jvm.
Another interesting option might be to create a http://www.quil.info/ based backend.
Or depending on what you're looking for, you might not need membrane at all, but the benefit of creating a quil based backend is that you get a bunch of membrane tools and components "for free".
Yeah, I was thinking about quil. I would like to implement more components for drawing. I'm also wondering if membrane could abstract over something like https://github.com/oakes/play-cljc. Making progress on the game loop. It's amazing how concise membrane can be:
(ns kimok.membrane-game
(:require [clojure.core.async :refer [<! timeout go-loop]]
[tick.core :as time]
[membrane.ui :as ui]
[membrane.skia :as skia]))
(def state (atom {:frame-number 0
:last-time (time/now)}))
(def window (skia/run #(ui/label @state)))
(defn on-frame []
(let [now (time/now)
{:keys [frame-number last-time]} @state
dt (time/micros (time/between last-time now))]
(reset! state {:frame-number (inc frame-number)
:fps (int (/ 1000000 dt))
:last-time now
:dt (float (/ dt 1000000))})))
(go-loop []
(<! (timeout 1000/60))
(on-frame)
((::skia/repaint window))
(recur))
Not sure if the metrics are accurate to what's really happening. It would be nice if the backend could pass them somehow.
I tried the same with java2d on 0.9.31.5-beta, but it still seems to wait on input events.@U02J388JDEG , I came across a stackoverflow post recently that said you might need to call https://docs.oracle.com/javase/7/docs/api/java/awt/Toolkit.html#sync() to get the screen to repaint on linux
oh wait, you're using the skia backend?
I'm sure you could extend support for membrane to play-clj.
when you say crash, do you mean "throw an exception and close the window" or jvm dies?
I've heard there's some issues on linux, but I've been unable to reproduce them
would you be able to share what you're using for clojure -Stree
?
org.clojure/clojure 1.10.3 . org.clojure/spec.alpha 0.2.194 . org.clojure/core.specs.alpha 0.2.56
I'm more interested in the which membrane version and which skialib you're using
org.clojure/clojure 1.11.1
. org.clojure/spec.alpha 0.3.218
. org.clojure/core.specs.alpha 0.2.62
net.sekao/odoyle-rules 0.11.0
. expound/expound 0.7.2
com.phronemophobic.membrane/skialib-linux-x86-64 0.9.31.0-beta
com.phronemophobic.membrane/skialib-macosx-aarch64 0.9.31.0-beta
com.phronemophobic.membrane/skialib-macosx-x86-64 0.9.31.0-beta
com.phronemophobic/membrane 0.9.31.8-beta
. cnuernber/dtype-next 8.041
. org.ow2.asm/asm 9.0
. insn/insn 0.5.2
. org.ow2.asm/asm 9.0
. camel-snake-kebab/camel-snake-kebab 0.4.2
. it.unimi.dsi/fastutil 8.2.1
. org.xerial.larray/larray-mmap 0.4.1
. org.xerial.larray/larray-buffer 0.4.1
. org.apache.commons/commons-math3 3.6.1
. org.roaringbitmap/RoaringBitmap 0.9.0
. org.roaringbitmap/shims 0.9.0
. com.github.wendykierp/JTransforms 3.1
X org.apache.commons/commons-math3 3.5 :older-version
. pl.edu.icm/JLargeArrays 1.5
X org.apache.commons/commons-math3 3.5 :older-version
. techascent/tech.resource 5.04
. org.clojure/tools.logging 1.1.0
. com.google.guava/guava 30.1.1-jre
. com.google.guava/failureaccess 1.0.1
. com.google.guava/listenablefuture 9999.0-empty-to-avoid-conflict-with-guava
. com.google.code.findbugs/jsr305 3.0.2
. org.checkerframework/checker-qual 3.8.0
. com.google.errorprone/error_prone_annotations 2.5.1
. com.google.j2objc/j2objc-annotations 1.3
. org.apache.commons/commons-text 1.9
. org.apache.commons/commons-lang3 3.11
. net.n01se/clojure-jna 1.0.0
X net.java.dev.jna/jna 4.0.0 :older-version
. net.java.dev.jna/jna 5.10.0
. com.rpl/specter 1.1.3
. riddley/riddley 0.1.12
. org.clojure/core.async 1.4.627
. org.clojure/tools.analyzer.jvm 1.2.0
. org.clojure/tools.analyzer 1.1.0
. org.clojure/core.memoize 1.0.236
. org.clojure/core.cache 1.0.207
. org.clojure/data.priority-map 1.0.0
X org.ow2.asm/asm 5.2 :older-version
. org.clojure/tools.reader 1.3.2
tick/tick 0.5.0-RC6
. com.widdindustries/cljc.java-time 0.1.21
. com.widdindustries/cljs.java-time 0.1.20
. cljsjs/js-joda-timezone 2.2.0-0
. cljsjs/js-joda-locale-en-us 3.1.1-1
. com.widdindustries/time-literals 0.1.10
when it crashed, did you switch between java2d and skia without restarting the repl?
the two backends are not friendly with eachother
I think that might work for a bit, but my guess is that might the cause leading to eventual crashes
Not sure if I can pinpoint the cause, but I'll let you know if I can reproduce something.
this works for me on swing. I'm wondering if adding a call to .sync
would help the java2d issues on linux
Something like:
(ns kimo
(:require [clojure.core.async :refer [<! timeout go-loop]
:as async]
[tick.core :as time]
[membrane.ui :as ui]
[membrane.java2d :as backend])
(:import java.awt.Toolkit))
(def default-toolkit (Toolkit/getDefaultToolkit))
(def state (atom {:frame-number 0
:last-time (time/now)}))
(defn on-frame [now]
(let [{:keys [frame-number last-time]} @state
dt (time/micros (time/between last-time now))]
(reset! state {:frame-number (inc frame-number)
:fps (int (/ 1000000 dt))
:last-time now
:dt (float (/ dt 1000000))})))
(defonce running? (atom false))
(comment
(def window (backend/run #(ui/label @state)))
(async/thread
(reset! running? true)
(loop []
(let [t (time/now)]
(on-frame t)
(async/<!! (timeout (- 1000/80
(/ (time/micros
(time/between t (time/now)))
1000))))
((::backend/repaint window))
(.sync ^Toolkit default-toolkit))
(when @running?
(recur))))
,
)
can you confirm that the call to .sync
is the fix?
if that's the case, I'll probably make an update to the java2d backend to call .sync
on repaint
By the way, still interested in figuring out how to build membrane/skia. I'd like to contribute to issues like https://github.com/phronmophobic/membrane/issues/23
if you're looking to build the native dependencies, you can check out the github workflow, https://github.com/phronmophobic/membrane/blob/master/.github/workflows/build.yml#L77
I think the issue with vsync is essentially a glfw issue (on mac osx)
on linux, it might be as simple as:
• setting the glfwswapinterval to 1 https://github.com/phronmophobic/membrane/blob/master/src/membrane/skia.clj#L1763, https://www.glfw.org/docs/latest/group__context.html#ga6d4e0cdf151b5e579bd67f13202994ed
• use glfwPollEvents
to wait for events instead of glfwWaitEventsTimeout
, see https://github.com/phronmophobic/membrane/blob/master/src/membrane/skia.clj#L1986
• for normal UI that doesn't normally change every frame, this will unnecessarily consume a bunch of cpu, so it's probably a good idea to only redraw if the view has changed since the last frame.
I'd be open to a pull request that makes this configurable as long as the defaults "just work" on linux and mac