1

我正在尝试将 lwjgl 与 clojure 一起用于游戏开发。

我的第一步是尝试在 REPL 的 OpenGL 屏幕上显示一些东西。在使用 lein repl 启动 repl 之后,这就是我到目前为止所做的:

(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)

请注意,如果做得足够快,这是可行的。但过了一会儿(即使我只是等待)我开始收到有关当前 OpenGL 上下文未绑定到当前线程的错误。

(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)

但也许最有趣的错误发生在我尝试调用 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)

看起来好像 repl 在一段时间不活动后随机产生了另一个线程。正如我所读到的,LWJGL 只允许您从最初创建它的线程进行 OpenGL 调用,所以我敢打赌这是导致这些错误的原因。

但是 REPL 怎么会随机切换线程呢?特别是如果我什么都不做,只是等待。

4

1 回答 1

3

这是一个已经针对 nREPL 项目报告的已知问题(并在 Clojure Google Group 上讨论​​过)。似乎 nREPL 使用终止空闲线程的线程池(可能根据keepalive设置)。

在修复之前,您可以使用解决方法(我承认有点尴尬):

(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))))

通过使用您自己的执行器,所有包装的代码on-executor都将在其线程中执行。newSingleThreadExecutor创建一个单线程,根据doc,仅当当前线程因异常而失败时才会替换它。当您尝试以长时间延迟执行最后一个表达式时,打印的线程 ID 应该保持不变。

请记住,shutdown在停止应用程序时您应该是执行者。

于 2016-03-23T19:55:43.297 回答