Trying to play with viscous locally, how can I bring skia in as a dependency?
@smith.adriane I'm happy to report this works and now I have a new fun one for you: try inspecting some pathological data structure like a tools.analyzer output then resize the window, program becomes functionally unresponsive
scratch that, resizing just behaves weird now
@ben.sless Interesting. Just tried some tools.analyzer output and didn't notice anything weird. Is there a repro I can follow?
Big hairy data structures like tools.analyzer output are the kinds of data that viscous was made for so if that use case isn't working well, I'd like to fix it!
The only datastructures I would consider truly pathological are infinitely empty sequences like (filter (constantly false) (range)) since there's no way to inspect it without calling first which loops forever.
It's every data structure, actually. Resize is just laggy
when you say resize, is that resizing the window or the viscous view?
The viscous view
Window resize is snappy
Is it slow for both skia and java2d or just skia backend?
Only checked skia
And do you recall if it was slow when shrinking and growing?
or possibly just growing?
Both
and just to double check, do you know if the data structures you were inspecting were fully realized?
Were any other operations laggy or just grow/shrink? • hovering over child data structures • navigating • popping • etc
I think they were I can try to record a demo today
that would be great
You'll probably be sleeping by the time I record it
Skia isn't required. If Skia isn't available, it should use java swing to display everything.
If you do want to use the skialib backend, you just need the skialib dependency that matches your OS and architecture. One of:
• com.phronemophobic.membrane/skialib-linux-x86-64 {:mvn/version "0.9.31.0-beta"}
• com.phronemophobic.membrane/skialib-macosx-aarch64 {:mvn/version "0.9.31.0-beta"}
• com.phronemophobic.membrane/skialib-macosx-x86-64 {:mvn/version "0.9.31.0-beta"}
You can also just add all 3 with no issue.
I have them on the class path but the toolkit isn't loaded
the skia toolkit? or any toolkit?
The skia toolkit, it starts with java2d instead
can you load (require 'membrane.skia) at the repl?
Tried it, not found
I can try to repro exactly when I'm at the computer and create a repository
ok, I think I found the issue
(defonce toolkit
#?(:clj
(if-let [tk (resolve 'membrane.skia/toolkit)]
@tk
@(requiring-resolve 'membrane.java2d/toolkit))
:cljs
nil))
pretty sure the resolve for skia should be requiring-resolve instead of resolve 😞err
maybe I did that on purpose
so that it only uses skia if that namespace has already been loaded
Aha, so requiring resolve should fix it?
right but that would almost always use skia
because I think the skia namespace loads even if skialib isn't available
I think I did this as a hack because I didn't expect anyone else besides me to use the skia version
and I like to default to skia
Java2d looks terrible on Ubuntu 😄
generally, loading a namespace isn't a big deal, but some UI libraries (cough AWT, GLFW) expect to be the only UI library to be running
hmmm, the issue is that I cheated and made monospaced, cell-width and cell-height top level defs. The right way to do it is to pass those values as arguments.
I can fix this, but I think my brain might be a little too tired to fix it tonight. I should be able to fix it tomorrow.
if you're running the code base locally, you can work around it by changing the toolkit definition in viscous.cljc to:
(require 'membrane.skia)
(defonce toolkit membrane.skia/toolkit)or just requiring membrane.skia anywhere before the toolkit in viscous.cljc gets initialized
@smith.adriane here you go
Did some very rough profiling
I have flame graphs to share
key insight is that path->spec dominates
paths keep getting recompiled. Can work be front-loaded?
@ben.sless thanks for the extra info! 🙏
Yep, that's no bueno.
I'm pretty surprised that it's showing path->spec as eating so many cycles. I'm sure path->spec could be more efficient, but that same code runs fine on my machine and in the browser, https://phronmophobic.github.io/viscous/.
How are you starting the inspector? Are you specifying your own version of specter? Is anything being printed to stdout?
I think I remember you saying you were running on linux? I'll see if I can reproduce that locally. I'm pretty puzzled by the symptoms so far. My initial guess is that some part of the event handling is running several times more often than it should be.
I ran this
(require 'membrane.skia)
(defonce toolkit membrane.skia/toolkit)
Then the example from READMELet me try to clear my cpcache
Let me double check running the example from the README. That's not how I usually run it.
same after clearing cpcache
anyway, I find the paths get recompiled constantly and it hits a dynamic var thing in specter which incurs a clojure.core/walk which takes forever and a half and generates more garbage than all social media combined
impressive
on-mouse-move 😄
Some dark magic in defui could probably help with precompiling them
in the end you're just emitting specter navigators, yes?
Right, so the question is why this is slow for you, but runs well enough in clj and cljs locally
Let me be paranoid for a second, I'll clone viscous again
I'm not assuming that you're doing anything weird. I'm assuming membrane is doing something weird on linux
like calling on-mouse-move several times for each event for some reason
or having on-mouse-move trigger a redraw which triggers an on-mouse-move or something
okay, I managed to recreate on a fresh clone
If I do this first, then load an example, it's laggy
(require 'membrane.skia)
(defonce toolkit membrane.skia/toolkit)
If I just load the viscous namespace it works fine
How can I tell which toolkit was used?
it should say if you print com.phronemophobic.viscous/toolkit
Right, so it lags for skia and not for java2d
ok, I have an idea. one sec
ok, nevermind. I'm confused again
What sort of profiling information can I get you that'll help?
I can produce everything from JFR recording to GC and JIT logs
I'd love to see the clojure -Stree for the project
just to make sure that we're using the same versions of things
org.clojure/clojure 1.10.3
. org.clojure/spec.alpha 0.2.194
. org.clojure/core.specs.alpha 0.2.56
com.phronemophobic.membrane/skialib-linux-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
Now you'll tell me I'm using a bad version of skia linuxnope, that's the latest one
I guess it might still be bad, but if it is, there's not a better one yet
😿
tragic
com.google.guava/listenablefuture 9999.0-empty-to-avoid-conflict-with-guava
😆So one of the things that is different is that locally, I use the monospaced Menlo font, but it should fall back to the system's monospaced if that's not available
I doubt that's it
however, the way to load the default monospaced font differs between java2d and skia. there's a way to do that correctly, but viscous currently does it the wrong way
really?
That wasn't it. Tried with a font I have installed, same
Looks nicer, though 🙂
and it actually use the new font? nice
yup, Source Code Pro
If it's going to be slow, it can at least be nice to look at
the spacing might be wrong since the sizes are hard-coded when the namespaces is loaded
fonts are annoying
ok, so there's some other difference between java2d and skia that is slowing things down a lot
There's a reason I stay away from UI/UX. You stumble in dark, bang little toe against couch. You had an experience. You're welcome
Yes, I can try to profile a bit, shake the Java tree, see what beans fall out
so if I could test this locally, I would just run something like:
(require '[membrane.ui :as ui])
(require '[membrane.skia :as backend]
;'[membrane.java2d :as backend]
)
(def pos (atom nil))
(defn test-mouse-move []
(ui/on
:mouse-move
(fn [pos']
(println pos)
(reset! pos pos')
nil)
[(ui/with-style ::ui/style-stroke
(ui/rectangle 400 400))
(ui/label @pos)]))
(backend/run #'test-mouse-move)and see if it's doing anything weird
see if 1) that minimal app is slow and 2) if it's printing out the same position multiple times.
1. yes 2. no
ok, so this isn't calling path->spec anywhere so the problem is elsewhere
Cool
I also added
(def ticker (java.util.concurrent.atomic.AtomicLong. 0))
(defn tick!
^long [^java.util.concurrent.atomic.AtomicLong n]
(let [m (.get n)
l (System/currentTimeMillis)]
(.set n l)
(- l m)))
I get pretty constant 25ms refresh rate
for the callback getting invoked, not the redraw
Is it capped at 40fps?
right now, it only redraws when there's an event. there's other ways to setup, but I think that's what it should be doing now
40 fps isn't impressive, but it shouldn't be unusable
okay
It's getting a bit late so I'm tapping out for today, I'll profile it tomorrow in more detail and let you know if I found something weird
ok, thanks again!
fyi, I tried to reproduce locally, but it seemed to run fine. fwiw, I'm running linux inside of virtualbox so it might not be the best environment to reproduce.
Not sure how interested you are in falling down this rabbit hole with me, so obviously, feel free to ignore. To recap, the main symptom we have is sluggish UI and key difference we have is seems to work except on some linuxs with skia. I know sluggishness is generally debugged with profiling, but there's almost no platform specific code inside of membrane. Most of the platform specific code is inside of the skia graphics library and the glfw windowing library. At this point, I'm assuming that the skia and glfw libraries are correct and that the problem is that I've written some non-portable code that uses those libraries incorrectly. I think it's much more likely that the problem is with the glfw integration rather than the skia integration. If the bug is an integration issue, profiling may or may not be that helpful. > I get pretty constant 25ms refresh rate This is pretty weird since I think the events happen on the same thread as the repaint (membrane doesn't particularly care, but I know mac OS does and I think it's the same on linux). So if events were triggering every 25ms, you would expect the screen to update regularly as well.
Another theory is that the screen isn't being redrawn for some reason. This snippet should print on every redraw:
(require '[membrane.ui :as ui])
(require '[membrane.skia :as backend]
;'[membrane.java2d :as backend]
)
(def pos (atom nil))
(defn on-draw [f]
(ui/no-events
(reify
ui/IOrigin
(-origin [elem]
[0 0])
ui/IBounds
(-bounds [elem]
[0 0])
backend/IDraw
(draw [this]
(f)))))
(defn test-mouse-move []
(ui/on
:mouse-move
(fn [pos']
#_(println pos')
(reset! pos pos')
nil)
[(ui/with-style ::ui/style-stroke
(ui/rectangle 400 400))
(on-draw #(println "draw!"))
(ui/label @pos)]))
(backend/run #'test-mouse-move)
@ben.sless fyi, just updated viscous on github. As long as you require membrane.skia before opening the inspector, it should use membrane.skia now.