[SOLVED] JVM macOS issue

Started by Arend-Jan, March 03, 2020, 15:33:53

Previous topic - Next topic

Arend-Jan

So I'm trying to develop a LWJGL application in Scala and build it with sbt. The challenge I'm facing is that my skeleton project seems to work on Linux, but not on macOS (crashes with a JVM fatal error).

The only difference between the Linux and macOS version is the reference to the native libraries (of lwjgl) in the build.sbt file. On both macOS and Linux I'm running openjdk 13 (on Mac already tried running it on 11 and 8 without success).

It seems to be crashing on "glfwCreateWindow", but I don't know why.

My question is: How do I properly debug this issue? Is it a problem in my code, JLWGL or the JVM?

For reference here is some of the out- and input.

The JVM fatal error
[info] Hello LWJGL 3.2.3 build 13!
[info] #
[info] # A fatal error has been detected by the Java Runtime Environment:
[info] #
[info] #  SIGSEGV (0xb) at pc=0x00007fff2caab4a0, pid=11896, tid=775
[info] #
[info] # JRE version: OpenJDK Runtime Environment (13.0.2+8) (build 13.0.2+8)
[info] # Java VM: OpenJDK 64-Bit Server VM (13.0.2+8, mixed mode, sharing, tiered, compressed oops, g1 gc, bsd-amd64)
[info] # Problematic frame:
[info] # C  [HIToolbox+0x274a0]  THIThemeTextInfoFinder::GetOptions() const+0x8
[info] #
[info] # No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
[info] #
[info] # An error report file with more information is saved as:
[info] # /<path to my project>/hs_err_pid11896.log
[info] #
[info] # If you would like to submit a bug report, please visit:
[info] #   http://bugreport.java.com/bugreport/crash.jsp



My build.sbt
ThisBuild / scalaVersion := "2.13.1"
ThisBuild / version      := "0.0.1"
fork := true
 lazy val testlwjgl = (project in file("."))
  .settings(
    name := "TestLWJGL",
    libraryDependencies ++= {
      val version = "3.2.3"
      val os = "macos" // TODO: Change to "windows", "linux" or "macos" if necessary

      Seq(
        "lwjgl",
        "lwjgl-glfw",
        "lwjgl-opengl"
        // TODO: Add more modules here
      ).flatMap {
        module => {
          Seq(
            "org.lwjgl" % module % version,
            "org.lwjgl" % module % version classifier s"natives-$os"
          )
        }
      }
    }
  )


My source code (scala file)

package mypackagename

import org.lwjgl._
import org.lwjgl.glfw._
import org.lwjgl.opengl._
import org.lwjgl.system._
import java.nio._
import org.lwjgl.glfw.Callbacks._
import org.lwjgl.glfw.GLFW._
import org.lwjgl.opengl.GL11._
import org.lwjgl.system.MemoryStack._
import org.lwjgl.system.MemoryUtil._


object HelloWorld {
  def main(args: Array[String]): Unit = {
    new HelloWorld().run()
  }
}

class HelloWorld { // The window handle
  private var window = 0L

  def run(): Unit = {
    System.out.println("Hello LWJGL " + Version.getVersion + "!")
    init()
    loop()
    // Free the window callbacks and destroy the window
    glfwFreeCallbacks(window)
    glfwDestroyWindow(window)
    // Terminate GLFW and free the error callback
    glfwTerminate
    glfwSetErrorCallback(null).free
  }

  private def init(): Unit = { // Setup an error callback. The default implementation
    // will print the error message in System.err.
    GLFWErrorCallback.createPrint(System.err).set
    // Initialize GLFW. Most GLFW functions will not work before doing this.
    if (!glfwInit) throw new IllegalStateException("Unable to initialize GLFW")
    // Configure GLFW
    glfwDefaultWindowHints // optional, the current window hints are already the default

    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE) // the window will stay hidden after creation

    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE) // the window will be resizable

    // Create the window
    window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL)
    if (window == NULL) throw new RuntimeException("Failed to create the GLFW window")
    // Setup a key callback. It will be called every time a key is pressed, repeated or released.

    glfwSetKeyCallback(window, (window: Long, key: Int, scancode: Int, action: Int, mods: Int) => {
      if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_RELEASE)) {
        glfwSetWindowShouldClose(window, true)
      }
    })
    // Get the thread stack and push a new frame
    try {
      val stack = stackPush
      try {
        val pWidth = stack.mallocInt(1) // int*
        val pHeight = stack.mallocInt(1)
        // Get the window size passed to glfwCreateWindow
        glfwGetWindowSize(window, pWidth, pHeight)
        // Get the resolution of the primary monitor
        val vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor)
        // Center the window
        glfwSetWindowPos(window, (vidmode.width - pWidth.get(0)) / 2, (vidmode.height - pHeight.get(0)) / 2)
      } finally if (stack != null) stack.close()
    } // the stack frame is popped automatically

    // Make the OpenGL context current
    glfwMakeContextCurrent(window)
    // Enable v-sync
    glfwSwapInterval(1)
    // Make the window visible
    glfwShowWindow(window)
  }

  private def loop(): Unit = { // This line is critical for LWJGL's interoperation with GLFW's
    // OpenGL context, or any context that is managed externally.
    // LWJGL detects the context that is current in the current thread,
    // creates the GLCapabilities instance and makes the OpenGL
    // bindings available for use.
    GL.createCapabilities
    // Set the clear color
    glClearColor(1.0f, 0.0f, 0.0f, 0.0f)
    // Run the rendering loop until the user has attempted to close
    // the window or has pressed the ESCAPE key.
    while ( {
      !glfwWindowShouldClose(window)
    }) {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) // clear the framebuffer

      glfwSwapBuffers(window) // swap the color buffers

      // Poll for window events. The key callback above will only be
      // invoked during this call.
      glfwPollEvents
    }
  }
}


Ps: I'm still working on optimizing for Scala properly, but want to fix this issue first.

Arend-Jan

So I solved the issue. The problem was that it wasn't started on the main thread, so i added -XstartOnFirstThread to my javaOptions.

Solution: change the build.sbt file to
ThisBuild / scalaVersion := "2.13.1"
ThisBuild / version      := "0.0.1"
javaOptions in run := Seq("-XstartOnFirstThread")
fork := true
 lazy val testlwjgl = (project in file("."))
  .settings(
    name := "TestLWJGL",
    libraryDependencies ++= {
      val version = "3.2.3"
      val os = "macos" // TODO: Change to "windows", "linux" or "macos" if necessary

      Seq(
        "lwjgl",
        "lwjgl-glfw",
        "lwjgl-opengl"
        // TODO: Add more modules here
      ).flatMap {
        module => {
          Seq(
            "org.lwjgl" % module % version,
            "org.lwjgl" % module % version classifier s"natives-$os"
          )
        }
      }
    }
  )


In version 3.2.0 there was a clear error messages which I could debug.
Quote[error] Caused by: java.lang.IllegalStateException: GLFW windows may only be created on the main thread and that thread must be the first thread in the process. Please run the JVM with -XstartOnFirstThread. For offscreen rendering, make sure another window toolkit (e.g. A
WT or JavaFX) is initialized before GLFW.
.
It would be great if we could have that back  ;D

spasi

The main thread check is still there in 3.2.3. How do you launch the application?

spasi