8000 GLFW_PLATFORM_ERROR on launching a simple lwjgl app · Issue #2680 · glfw/glfw · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

GLFW_PLATFORM_ERROR on launching a simple lwjgl app #2680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Martmists-GH opened this issue Feb 3, 2025 · 7 comments
Open

GLFW_PLATFORM_ERROR on launching a simple lwjgl app #2680

Martmists-GH opened this issue Feb 3, 2025 · 7 comments
Assignees
Labels
bug Bug reports and bugfix pull requests external Issues GLFW cannot reasonably solve Wayland

Comments

@Martmists-GH
Copy link
Martmists-GH commented Feb 3, 2025

Error:

[LWJGL] GLFW_PLATFORM_ERROR error
	Description : EGL: Failed to clear current context: An EGLDisplay argument does not name a valid EGL display connection
	Stacktrace  :
		org.lwjgl.glfw.GLFW.nglfwCreateWindow(GLFW.java:2086)
		org.lwjgl.glfw.GLFW.glfwCreateWindow(GLFW.java:2251)
		Engine.<init>(Main.kt:45)
		MainKt.main(Main.kt:172)
		MainKt.main(Main.kt)

Problem:

After printing the above error, Wayland does seem to create a window according to the taskbar, but it's permanently invisible and can't be focused, doesn't respond to keystrokes, etc.

Code snippet used
import org.lwjgl.*
import org.lwjgl.glfw.*
import org.lwjgl.opengl.*
import org.lwjgl.system.MemoryStack
import org.joml.*
import org.lwjgl.system.MemoryUtil
import java.nio.FloatBuffer

class Engine {
    private val windowHandle: Long
    private var shaderProgram: Int = 0
    private var vao: Int = 0
    private var vbo: Int = 0

    companion object {
        const val WIDTH = 800
        const val HEIGHT = 600
        const val TITLE = "WIP Engine"

        // Vertex data for triangle (position x, y, z)
        private val VERTICES = floatArrayOf(
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
            0.0f, 0.5f, 0.0f
        )
    }

    init {
        GLFWErrorCallback.createPrint(System.err).set()

        // Initialize GLFW
        if (!GLFW.glfwInit()) {
            throw IllegalStateException("Unable to initialize GLFW")
        }

        // Configure window
        GLFW.glfwDefaultWindowHints()
        GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE)
        GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_FALSE)
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3)
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 3)
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE)

        // Create window
        windowHandle = GLFW.glfwCreateWindow(WIDTH, HEIGHT, TITLE, 0, 0)
        if (windowHandle == MemoryUtil.NULL) {
            throw IllegalStateException("Failed to create GLFW window")
        }

        // Set up key callback
        GLFW.glfwSetKeyCallback(windowHandle) { window, key, _, action, _ ->
            if (key == GLFW.GLFW_KEY_ESCAPE && action == GLFW.GLFW_PRESS) {
                GLFW.glfwSetWindowShouldClose(window, true)
            }
        }

        // Make OpenGL context current
        GLFW.glfwMakeContextCurrent(windowHandle)
        GLFW.glfwShowWindow(windowHandle)
        GLFW.glfwSwapInterval(1)
        GL.createCapabilities()

        // Initialize shaders and buffers
        initShaders()
        initVAO()
    }

    private fun initShaders() {
        // Vertex shader
        val vertexShader = GL30.glCreateShader(GL30.GL_VERTEX_SHADER)
        GL30.glShaderSource(vertexShader, """
            #version 330 core
            layout (location = 0) in vec3 aPos;
            
            void main() {
                gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
            }
        """.trimIndent())
        GL30.glCompileShader(vertexShader)
        checkShaderCompile(vertexShader)

        // Fragment shader
        val fragmentShader = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER)
        GL30.glShaderSource(fragmentShader, """
            #version 330 core
            out vec4 FragColor;
            
            void main() {
                FragColor = vec4(1.0, 1.0, 1.0, 1.0);
            }
        """.trimIndent())
        GL30.glCompileShader(fragmentShader)
        checkShaderCompile(fragmentShader)

        // Shader program
        shaderProgram = GL30.glCreateProgram()
        GL30.glAttachShader(shaderProgram, vertexShader)
        GL30.glAttachShader(shaderProgram, fragmentShader)
        GL30.glLinkProgram(shaderProgram)
        checkProgramLink()

        // Clean up shaders
        GL30.glDeleteShader(vertexShader)
        GL30.glDeleteShader(fragmentShader)
    }

    private fun initVAO() {
        // Generate and bind VAO
        vao = GL30.glGenVertexArrays()
        GL30.glBindVertexArray(vao)

        // Generate and bind VBO
        vbo = GL30.glGenBuffers()
        GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo)

        // Upload vertex data
        MemoryStack.stackPush().use { stack ->
            val buffer = stack.mallocFloat(VERTICES.size)
            buffer.put(VERTICES)
            buffer.flip()
            GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW)
        }

        // Set vertex attribute pointers
        GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0)
        GL30.glEnableVertexAttribArray(0)

        // Unbind buffers
        GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0)
        GL30.glBindVertexArray(0)
    }

    fun run() {
        GL30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)

        while (!GLFW.glfwWindowShouldClose(windowHandle)) {
            GL30.glClear(GL30.GL_COLOR_BUFFER_BIT)

            GL30.glUseProgram(shaderProgram)
            GL30.glBindVertexArray(vao)
            GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, 3)

            GLFW.glfwSwapBuffers(windowHandle)
            GLFW.glfwPollEvents()
        }

        cleanup()
    }

    private fun cleanup() {
        GL30.glDeleteVertexArrays(vao)
        GL30.glDeleteBuffers(vbo)
        GL30.glDeleteProgram(shaderProgram)
        GLFW.glfwDestroyWindow(windowHandle)
        GLFW.glfwTerminate()
    }

    private fun checkShaderCompile(shader: Int) {
        if (GL30.glGetShaderi(shader, GL30.GL_COMPILE_STATUS) == GL30.GL_FALSE) {
            throw IllegalStateException("Shader compilation failed: ${GL30.glGetShaderInfoLog(shader)}")
        }
    }

    private fun checkProgramLink() {
        if (GL30.glGetProgrami(shaderProgram, GL30.GL_LINK_STATUS) == GL30.GL_FALSE) {
            throw IllegalStateException("Program linking failed: ${GL30.glGetProgramInfoLog(shaderProgram)}")
        }
    }
}

fun main() {
    val engine = Engine()
    engine.run()
}
@coderedart
Copy link

I can reproduce this on my system (arch, kde, wayland, all latest.) too. I tried, but failed to find the cause. But posting the progress here anyway, as it might be useful.

With the normal glfw (installed by system or built from this repo), everything works fine. I tried a minimal C example and it all works fine.

Run lwjgl with system glfw

So, I decided to run the kotlin example with system's libglfw.so using the following jvm options:

  • -Dorg.lwjgl.util.Debug=true
  • -Dorg.lwjgl.util.DebugLoader=true
  • -Dorg.lwjgl.glfw.libname=/usr/lib64/libglfw.so

I can verify that system library is chosen, but due to lacking the IME symbols, it fails to load.

[LWJGL] Loading library: /usr/lib64/libglfw.so
	Module: org.lwjgl.glfw
	Success
Exception in thread "main" java.lang.ExceptionInInitializerError
...
Caused by: java.lang.NullPointerException: A required function is missing: glfwGetPreeditCursorRectangle
	at org.lwjgl.system.APIUtil.requiredFunctionMissing(APIUtil.java:147)
...

Check lwjgl's glfw fork

Anyway, I cloned https://github.com/LWJGL-CI/glfw , which seems to be the fork that lwjgl uses. I thought the fork might be the problem. I tried building and running the examples, and everything worked fine. So, I created a tiny main.c example to check and everything still works fine. So, the problem might be lwjgl's libglfw.so that is downloaded from cloud.

Try running kotlin example with lwjgl's glfw fork (custom built)

So, I updated the options to point to the new libglfw.so file that I built:

*-Dorg.lwjgl.glfw.libname=/home/user/projects/glfw/libglfw.so (this is built from lwjgl's fork)

And it loads successfully (yay, so I am loading the fork's library with IME symbols linked):

[LWJGL] Loading library: /home/user/projects/glfw/libglfw.so
	Module: org.lwjgl.glfw
	Success

Still a failure? More debugging

But we get the same egl error again.

After editing the glfw source to add more context to errors, I found that the error triggers when setting the current context to null. So, the callstack leading to error is: glfwCreateWindow -> _glfwCreateWindowWayland -> _glfwRefreshContextAttribs -> glfwMakeContextCurrent to make window current and then, set the previous current (which is null) -> makeContextCurrentEGL -> eglMakeCurrent .

The same _glfw.egl.display pointer is used to set the window current and to detach the current context (by setting the null as current at the end) in _glfwRefreshContextAttribs. BUT, the error ONLY triggers if we are trying to set null as current context.

Anyway, I tried to reduce the variables and came up with this short snippet.

import org.lwjgl.glfw.*
import org.lwjgl.system.MemoryUtil

fun main() {
    GLFWErrorCallback.createPrint(System.err).set()
    if (!GLFW.glfwInit()) {
        println("Unable to initialize GLFW")
    }
    println(GLFW.getLibrary().path)
    val window = GLFW.glfwCreateWindow(800, 600, "Foo", 0, 0)
    if (window == MemoryUtil.NULL) {
        println("Failed to create GLFW window")
    }
    println("making $window current")
    GLFW.glfwMakeContextCurrent(window)
    println("making null current")
    GLFW.glfwMakeContextCurrent(0);
}

If you run this example, you will see the error only trigger once at window creation and then, once at "making null current". Just for sanity, I also checked if it was running on main-thread with Thread.currentThread().name == "main" and it was.

I also verified that choosing x11 platform GLFW.glfwInitHint(GLFW.GLFW_PLATFORM, GLFW.GLFW_PLATFORM_X11) makes this go away. But, if I chose GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_CREATION_API, GLFW.GLFW_EGL_CONTEXT_API) egl with X11, I get the same error as choosing wayland:

/home/user/projects/glfw/libglfw.so
[LWJGL] GLFW_PLATFORM_ERROR error
	Description : EGL: Failed to clear current context with display 0x739f10546880: An EGLDisplay argument does not name a valid EGL display connection
	Stacktrace  :
		org.lwjgl.glfw.GLFW.nglfwCreateWindow(GLFW.java:2086)
		...
making 127127008800752 current
making null current
[LWJGL] GLFW_PLATFORM_ERROR error
	Description : EGL: Failed to clear current context with display 0x739f10546880: An EGLDisplay argument does not name a valid EGL display connection
	Stacktrace  :
		org.lwjgl.glfw.GLFW.glfwMakeContextCurrent(GLFW.java:5186)
		....

I don't know kotlin/java, so, this is as far as I can go. The error only triggers with kotlin/jvm, while I can verify that using C (or Rust) has no problems on wayland (or x11 + egl).

@coderedart
Copy link

@dougbinks This is a duplicate of #2510 (same error, with more comments)

@coderedart
Copy link

According to libgdx/gdx-liftoff#206 , this might be a wayland + nvidia specific issue?

The solution is to set env var __GL_THREADED_OPTIMIZATIONS=0. I can confirm that doing so makes the error disappear.

I can't believe I wasted so much time on debugging this, and it turns out to be a nvidia driver bug 😭

I think this issue can be closed or maybe merged with #2510 .

@dougbinks
Copy link
Contributor

. The error only triggers with kotlin/jvm, while I can verify that using C (or Rust) has no problems on wayland (or x11 + egl).

From what I can understand this issue does not occur when using GLFW with C, and thus could well be an LWJGL specific issue. For the moment I will mark this as external unless it can be replicated with pure C code.

@dougbinks dougbinks self-assigned this Mar 10, 2025
@dougbinks dougbinks added bug Bug reports and bugfix pull requests Wayland external Issues GLFW cannot reasonably solve labels Mar 10, 2025
@Spasi
Copy link
Contributor
Spasi commented Mar 10, 2025

@dougbinks This bug is not LWJGL-specific. It reproduces in upstream GLFW with:

__GL_THREADED_OPTIMIZATIONS=1 ./build/examples/triangle-opengl (on Ubuntu 24.10 with Wayland session)

The only difference seems to be that, whatever heuristic the Nvidia driver uses to enable threaded optimizations defaults to enabled with Java applications, but defaults to disabled with the GLFW examples.

@dougbinks
Copy link
Contributor

Thanks, that's useful extra information - I think this is still external then, but likely a driver issue.

@Martmists-GH
Copy link
Author

I can reproduce those results, turning off that env var makes it run fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bug reports and bugfix pull requests external Issues GLFW cannot reasonably solve Wayland
Projects
None yet
Development

No branches or pull requests

4 participants
0