FlorianRhiem / pyGLFW

Python bindings for GLFW
MIT License
232 stars 36 forks source link

Can't create window on Wayland 3.36 #49

Closed a-anjos closed 4 years ago

a-anjos commented 4 years ago

Hi Florian, Using pyGLFW 1.11 on Wayland I get the following message when creating a window: ...

  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 1156, in create_window
    return _glfw.glfwCreateWindow(width, height, _to_char_p(title),
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 616, in errcheck
    _reraise(exc[1], exc[2])
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 52, in _reraise
    raise exception.with_traceback(traceback)
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 595, in callback_wrapper
    return func(*args, **kwargs)
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 808, in _handle_glfw_errors
    raise GLFWError(message)
glfw.GLFWError: (65544) b'Wayland: Focusing a window requires user interaction'

I've tried the "fix" I've found on one of the solved issues:

if sys.platform.startswith("linux"):
            if "wayland" in os.getenv("XDG_SESSION_TYPE", "").lower():
                glfw.window_hint(glfw.FOCUSED, False)

However, I get the following:

File "/home/aanjos/Documents/git_sandbox/g3D/g3D/Drawable.py", line 36, in vbo_vertex_data
    gl.glVertexAttribPointer(layout_pos, 3, gl.GL_FLOAT, False, 0, None)

  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/latebind.py", line 63, in __call__
    return self.wrapperFunction( self.baseFunction, *args, **named )

  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer
    contextdata.setValue( key, array )

  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/contextdata.py", line 58, in setValue
    context = getContext( context )

  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/contextdata.py", line 40, in getContext
    raise error.Error(Error: Attempt to retrieve context when no valid context

Everything was working on Ubuntu 20.04. I've installed Debian testing today because it has the Linux kernel 5.5 and I got this problem.

I have libgfw3-wayland 3.3.2-1.

Thanks, António

FlorianRhiem commented 4 years ago

Hello António, can you please run this (almost) minimal example to see whether a context can be created?

import glfw
from OpenGL import GL

glfw.ERROR_REPORTING = 'warn'

if not glfw.init():
    print("init failed")
    exit(1)
window = glfw.create_window(100, 100, "Hello World", None, None)
if not window:
    print("create_window failed")
    exit(2)

glfw.make_context_current(window)

print(GL.glGetString(GL.GL_VERSION))
a-anjos commented 4 years ago

Hi Florian,

Here goes:

/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py:810: GLFWError: (65544) b'Wayland: Focusing a window requires user interaction'
  warnings.warn(message, GLFWError)
b'3.0 Mesa 19.3.3'

Thanks, António

FlorianRhiem commented 4 years ago

Thank you, this looks like a window was created successfully and an OpenGL context is bound to it. So it's unlikely to be an issue with GLFW or this wrapper.

Could it be that you forgot to call glfw.make_context_current(window) in your actual code?

a-anjos commented 4 years ago

Dear Florian,

No, I didn't:

        self.world = world
        self.width = width
        self.height = height
        log.debug('Initialize context')
        glfw.init()

        glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
        glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 6)
        glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
        glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
        glfw.window_hint(glfw.RESIZABLE, False)

        #if sys.platform.startswith("linux"):
        #    if "wayland" in os.getenv("XDG_SESSION_TYPE", "").lower():
        #        glfw.window_hint(glfw.FOCUSED, False)

        self.win = glfw.create_window(width, height, '3D world', None, None)

        glfw.make_context_current(self.win)  # set current context
        glfw.set_key_callback(self.win, self.on_key)  # event handling

        print('OpenGL:', gl.glGetString(gl.GL_VERSION))
        #print('GLSL:', gl.glGetString(gl.GL_SHADING_LANGUAGE_VERSION))
        print('Renderer:', gl.glGetString(gl.GL_RENDERER).decode())

        gl.glClearColor(*bg)
        gl.glEnable(gl.GL_DEPTH_TEST)
        self.fill_modes = cycle([gl.GL_LINE, gl.GL_POINT, gl.GL_FILL])
        self.cull = True
        gl.glEnable(gl.GL_CULL_FACE)

Like I said, it was working perfectly on Ubuntu 20.04 (I didn't make note of the versions of glfw, etc., I was using there). Maybe a problem with Wayland?

Thanks, António

FlorianRhiem commented 4 years ago

The PyOpenGL error you reported earlier states that it was caused by the lack of a current, valid OpenGL context. If you can create an OpenGL context and make it current, I would guess that it either wasn't current at the time, somehow became invalid or was lost completely. I suggest that you create a minimal working example of this and debug it from there.

In any case, I don't see any relation between the issue and this wrapper around GLFW, so I'll close this.

physkets commented 4 years ago

I'm having the same issue, with no valid context being found during glVertexAttribPointer() even though I do create it. I am also on Wayland. This is the error I get:

Traceback (most recent call last):
  File "first.py", line 121, in <module>
    gl.glVertexAttribPointer(LOCATION, 2, gl.GL_FLOAT, False, STRIDE, OFFSET)
  File "/usr/lib/python3.8/site-packages/OpenGL/latebind.py", line 63, in __call__
    return self.wrapperFunction( self.baseFunction, *args, **named )
  File "/usr/lib/python3.8/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer
    contextdata.setValue( key, array )
  File "/usr/lib/python3.8/site-packages/OpenGL/contextdata.py", line 58, in setValue
    context = getContext( context )
  File "/usr/lib/python3.8/site-packages/OpenGL/contextdata.py", line 40, in getContext
    raise error.Error(
OpenGL.error.Error: Attempt to retrieve context when no valid context

@FlorianRhiem, of course, it does not out-right seem like an issue with pyGLFW, but it would be nice if we could resolve it here.

FlorianRhiem commented 4 years ago

Do you have a minimal working example for this?

a-anjos commented 4 years ago

Dear Florian,

I cannot test this (my glfw/opengl is not working - maybe you could, please?), however, I suppose it's working. I took it from here: https://metamost.com/opengl-with-python/:

import contextlib, sys
from OpenGL import GL as gl
import glfw

@contextlib.contextmanager
def create_main_window():
    if not glfw.init():
        sys.exit(1)
    try:
        glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
        glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
        glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
        glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)

        title = 'Tutorial 2: First Triangle'
        window = glfw.create_window(500, 400, title, None, None)
        if not window:
            sys.exit(2)
        glfw.make_context_current(window)

        glfw.set_input_mode(window, glfw.STICKY_KEYS, True)
        gl.glClearColor(0, 0, 0.4, 0)

        yield window

    finally:
        glfw.terminate()

if __name__ == '__main__':
    with create_main_window() as window:
        while (
            glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS and
            not glfw.window_should_close(window)
        ):
            gl.glClear(gl.GL_COLOR_BUFFER_BIT)
            glfw.swap_buffers(window)
            glfw.poll_events()

The result is the same:

Traceback (most recent call last):
  File "test.py", line 32, in <module>
    with create_main_window() as window:
  File "/usr/lib/python3.8/contextlib.py", line 113, in __enter__
    return next(self.gen)
  File "test.py", line 18, in create_main_window
    window = glfw.create_window(500, 400, title, None, None)
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 1156, in create_window
    return _glfw.glfwCreateWindow(width, height, _to_char_p(title),
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 616, in errcheck
    _reraise(exc[1], exc[2])
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 52, in _reraise
    raise exception.with_traceback(traceback)
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 595, in callback_wrapper
    return func(*args, **kwargs)
  File "/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py", line 808, in _handle_glfw_errors
    raise GLFWError(message)
glfw.GLFWError: (65544) b'Wayland: Focusing a window requires user interaction'

Thanks, António

FlorianRhiem commented 4 years ago

@a-anjos I do not have a Wayland system at the moment. Your code is missing the glfw.ERROR_REPORTING = 'warn' to prevent the Wayland GLFWError from causing an exception and glVertexAttribPointer isn't even used in it.

a-anjos commented 4 years ago

Dear Florian,

Right. Here goes another example I've found here: http://morpheo.inrialpes.fr/~franco/3dgraphics/_downloads/eade9051662493ff8d990dddc139677f/

test.zip

The result is:

/home/aanjos/.local/lib/python3.8/site-packages/glfw/__init__.py:810: GLFWError: (65544) b'Wayland: Focusing a window requires user interaction'
  warnings.warn(message, GLFWError)
OpenGL 4.6 (Core Profile) Mesa 19.3.3, GLSL 4.60, Renderer Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2) 
Traceback (most recent call last):
  File "viewer.py", line 173, in <module>
    main()                     # main function keeps variables locally scoped
  File "viewer.py", line 164, in main
    viewer.add(SimpleTriangle(color_shader))
  File "viewer.py", line 76, in __init__
    GL.glVertexAttribPointer(0, 3, GL.GL_FLOAT, False, 0, None)
  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/latebind.py", line 63, in __call__
    return self.wrapperFunction( self.baseFunction, *args, **named )
  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer
    contextdata.setValue( key, array )
  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/contextdata.py", line 58, in setValue
    context = getContext( context )
  File "/home/aanjos/.local/lib/python3.8/site-packages/OpenGL/contextdata.py", line 40, in getContext
    raise error.Error(
OpenGL.error.Error: Attempt to retrieve context when no valid context

Cheers, António

FlorianRhiem commented 4 years ago

If this lets you reproduce the error, then please reduce it to a minimal working example.

a-anjos commented 4 years ago

@FlorianRhiem This is quite minimal, it just draws a triangle.

FlorianRhiem commented 4 years ago

I doubt that it needs to draw a triangle to reproduce the issue, neither will it need classes, etc.

Like I said, I do not have a Wayland system available and I do not think that the issue is related to my work. I can try to guide you towards finding the cause of this issue, but I cannot do the work for you.

a-anjos commented 4 years ago

I'd gladly simplify the code (or produce a minimal version), however, since I have no system where I could run it, it would be hard to not introduce lots of bugs. Thanks though.

physkets commented 4 years ago

Okay, here is the smallest, complete program that produces this. It is modified from a tutorial example. I'm not sure if there is anything extraneous. The error occurs at line 51, with the function: glVertexAttribPointer()

from ctypes import c_void_p
import glfw
import OpenGL.GL as gl
from numpy import zeros, float32

glfw.init()
glfw.ERROR_REPORTING = 'warn'
glfw.window_hint(glfw.FOCUSED, False) # prevent Wayland focusing warning

WINDOW = glfw.create_window(512, 512, "Hello World", None, None)

glfw.make_context_current(WINDOW)

PROGRAM = gl.glCreateProgram()
VERTEX = gl.glCreateShader(gl.GL_VERTEX_SHADER)
FRAGMENT = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)

VERTEX_CODE = """
    #version 460
    in vec2 position;
    void main(){ gl_Position = vec4(position, 0.0, 1.0); } """

FRAGMENT_CODE = """
    #version 460
    out vec4 FragColor;
    void main() { FragColor = vec4(1.0, 0.0, 0.0, 1.0); } """

gl.glShaderSource(VERTEX, VERTEX_CODE)
gl.glShaderSource(FRAGMENT, FRAGMENT_CODE)

gl.glCompileShader(VERTEX)
gl.glCompileShader(FRAGMENT)

gl.glAttachShader(PROGRAM, VERTEX)
gl.glAttachShader(PROGRAM, FRAGMENT)
gl.glLinkProgram(PROGRAM)
gl.glDetachShader(PROGRAM, VERTEX)
gl.glDetachShader(PROGRAM, FRAGMENT)

gl.glUseProgram(PROGRAM)

DATA = zeros(4, [("position", float32, 2)])
GPU_BUFFER = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, GPU_BUFFER)

STRIDE = DATA.strides[0]
OFFSET = c_void_p(0)
LOCATION = gl.glGetAttribLocation(PROGRAM, "position")
gl.glEnableVertexAttribArray(LOCATION)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, GPU_BUFFER)
gl.glVertexAttribPointer(LOCATION, 2, gl.GL_FLOAT, False, STRIDE, OFFSET)

glfw.terminate()
FlorianRhiem commented 4 years ago

Thank you, @physkets. If you leave out the shader program and the vertex array object, does it still produce the error?

Basically this:

from ctypes import c_void_p
import glfw
import OpenGL.GL as gl

glfw.init()
glfw.ERROR_REPORTING = 'warn'
glfw.window_hint(glfw.FOCUSED, False) # prevent Wayland focusing warning
WINDOW = glfw.create_window(512, 512, "Hello World", None, None)
glfw.make_context_current(WINDOW)

LOCATION = 0
gl.glVertexAttribPointer(LOCATION, 2, gl.GL_FLOAT, False, 0, c_void_p(0))

If this doesn't cause the error, does an OpenGL error (namely GL_INVALID_VALUE) occur when you pass an invalid value for LOCATION, e.g. LOCATION = gl.GL_MAX_VERTEX_ATTRIBS or does it cause the issue then?

Also, please check whether the context is still there after it has been reported as missing, e.g. by wrapping the glVertexAttribPointer call with a try-except-pass and then adding print(gl.glGetString(gl.GL_VERSION)).

Also, what happens if you add the following to the beginning of a script that causes the issue:

import OpenGL
OpenGL.CHECK_CONTEXT = False

This disables the context checking done by PyOpenGL, which is likely to be the source of the error message.

physkets commented 4 years ago

The same error (no valid context) occurs even when using that snippet (without the shader program).

Catching the exception like this:

try:
    gl.glVertexAttribPointer(LOCATION, 2, gl.GL_FLOAT, False, 0, c_void_p(0))
except:
    print(gl.glGetString(gl.GL_VERSION))

prints out: b'4.6 (Compatibility Profile) Mesa 20.0.4'.

Setting the context check to False still leaves me with the same error:
OpenGL.error.Error: Attempt to retrieve context when no valid context.

FlorianRhiem commented 4 years ago

Alright, then the issue is definitely with the function loading and context checking of PyOpenGL.

What does the following return before (and possibly after) the call to glVertexAttribPointer?

from OpenGL import platform
print(platform.PLATFORM)
print(platform.GetCurrentContext())
physkets commented 4 years ago

Firstly, my syntax checker tells me:

no-member: Module 'OpenGL.platform' has no 'PLATFORM' member
no-member: Module 'OpenGL.platform' has no 'GetCurrentContext' member

And if I go ahead anyway, I see the following output, both before and after the call to glVertexAttribPointer():

<OpenGL.platform.glx.GLXPlatform object at 0x7f6623fd0be0>
0

Shouldn't I be using EGL on Wayland, instead of GLX?

FlorianRhiem commented 4 years ago

PyOpenGL does some trickery to load the actual platform and fill in the namespace of OpenGL.platform, that's likely the reason your checker complains. And the fact that GetCurrentContext is the reason glVertexAttribPointer doesn't work, though as you say, I also would've expected a OpenGL.platform.egl.EGLPlatform object for Wayland. Can you please set the environment variable PYOPENGL_PLATFORM to egl, either by setting it on the command line or by adding the following to the beginning of your script?

import os
os.environ['PYOPENGL_PLATFORM'] = 'egl'
physkets commented 4 years ago

When I set that environment variable, the outputs are:

<OpenGL.platform.egl.EGLPlatform object at 0x7ff271373c10>
275506848

And there is no error in the minimal example. In my actual code, I still get some errors at the same glVertexAttribPointer(), but that seems like something else.

So that's that. It was just a matter of the wrong platform being chose. Thanks a lot Florian.
Isn't there some way this can be checked for, and a more relevant error is reported, in pyGLFW? (So someone else doesn't have to go through all this to figure it out) Or would it be more appropriate in pyOpenGL?

FlorianRhiem commented 4 years ago

Yes, it could be fixed in pyGLFW with something like this:

 if "wayland" in os.getenv("XDG_SESSION_TYPE", "").lower() and not os.environ.get("PYOPENGL_PLATFORM", ""):
    os.environ["PYOPENGL_PLATFORM"] = "egl"

However I would strongly prefer if this is actually fixed in PyOpenGL as opposed to here, as it has nothing to do with pyGLFW specifically and very, very little with GLFW in general. As far as I am aware, EGL is the OpenGL context creation API for Wayland, so using it as default for Wayland sessions seems generally reasonable when GLX is the only real alternative.

physkets commented 4 years ago

I was already discussing this on the pyOpenGL mailinglist:
https://sourceforge.net/p/pyopengl/mailman/pyopengl-users/thread/M5HjopY--3-2%40tutanota.com/

I think I might file a bug report with them, with that solution you just wrote.