1
votes

I'm trying to use lwjgl with clojure for game developement.

My first step is trying to display something on an OpenGL screenfrom the REPL. After launching the repl with lein repl this is what I have done so far:

(import org.lwjgl.opengl GL11 Display DisplayMode
(Display/setDisplayMode (DisplayMode. 800 600))
(Display/create) ; This shows a black 800x600 window as expected
(GL11/glClearColor 1.0 0.0 0.0 1.0) 
(GL11/glClear (bit-or GL11/GL_COLOR_BUFFER_BIT GL11/GL_DEPTH_BUFFER_BIT))
(Display/update)

Note that this works, if done quick enough. But after a while (even if I just wait) I start getting errors about the current OpenGL context not being bound to the current thread.

(Display/update)
IllegalStateException No context is current  org.lwjgl.opengl.LinuxContextImplementation.swapBuffers (LinuxContextImplementation.java:72)

(GL11/glClear ...) 
RuntimeException No OpenGL context found in the current thread.  org.lwjgl.opengl.GLContext.getCapabilities (GLContext.java:124)

But maybe the most intriguing of the errors happens when I try to call Display/destroy

(Display/destroy)
IllegalStateException From thread Thread[nREPL-worker-4,5,main]: Thread[nREPL-worker-0,5,] already has the context current  org.lwjgl.opengl.ContextGL.checkAccess (ContextGL.java:184)

It all looks as if the repl randomly spawned another thread after some time of inactivity. As I've been able to read, LWJGL only lets you make OpenGL calls from the thread from where it originally was created, so I bet this is causing those errors.

But how could the REPL be randomly switching threads? And especially if I'm not doing anything, just waiting.

1

1 Answers

3
votes

It's a known issue already reported against nREPL project (and discussed on Clojure Google Group). It seems that nREPL uses thread pool which terminates idle threads (probably according to keepalive setting).

Until it's fixed you can use a workaround for this (a bit awkward, I admit):

(import '(java.util.concurrent Executors))

(def opengl-executor (Executors/newSingleThreadExecutor))

(defmacro with-executor [executor & body]
  `(.submit ~executor (fn [] ~@body)))

(on-executor opengl-executor
  (println (.getId (Thread/currentThread))))

By using your own executor, all code wrapped in on-executor will be executed in its thread. newSingleThreadExecutor creates one single thread which according to doc will replace it only when the current one fails due to exception. When you try to execute the last expression with long delays, the printed thread ID should remain the same.

Remember that you should shutdown the executor when stopping your application.