Skip to content
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

pyglet.gl.lib.GLException when trying to draw text while multiple windows are open #1102

Open
ItsVeeBot opened this issue May 6, 2024 · 7 comments
Labels
bug Something isn't working

Comments

@ItsVeeBot
Copy link

Describe the bug
When opening multiple windows and attempting to draw text to one of them, Pyglet throws a pyglet.gl.lib.GLException:

Traceback (most recent call last):
  File "D:\VideoKaraokePrototype\Proto2\pyglet_multi_windows.py", line 16, in <module>
    pyglet.app.run()
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\app\__init__.py", line 76, in run
    event_loop.run(interval)
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\app\base.py", line 163, in run
    timeout = self.idle()
              ^^^^^^^^^^^
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\app\base.py", line 224, in idle
    self.clock.call_scheduled_functions(dt)
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\clock.py", line 217, in call_scheduled_functions
    item.func(now - item.last_ts, *item.args, **item.kwargs)
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\app\base.py", line 118, in _redraw_windows
    window.dispatch_event('on_draw')
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\window\__init__.py", line 672, in dispatch_event
    super().dispatch_event(*args)
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\event.py", line 380, in dispatch_event
    if handler(*args):
       ^^^^^^^^^^^^^^
  File "D:\VideoKaraokePrototype\Proto2\pyglet_multi_windows.py", line 13, in on_draw
    instructions_label.draw()
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\text\layout\base.py", line 1422, in draw
    self._batch.draw()
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\graphics\__init__.py", line 409, in draw
    func()
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\graphics\__init__.py", line 338, in <lambda>
    draw_list.append((lambda d, m: lambda: d.draw(m))(domain, mode))
                                           ^^^^^^^^^
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\graphics\vertexdomain.py", line 395, in draw
    self.vao.bind()
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\graphics\vertexarray.py", line 23, in bind
    glBindVertexArray(self._id)
  File "C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\gl\lib.py", line 78, in errcheck
    raise GLException(f'(0x{error}): {msg}')
pyglet.gl.lib.GLException: (0x1282): Invalid operation. The specified operation is not allowed in the current state.

System Information:

Platform
------------------------------------------------------------------------------
platform:  Windows-11-10.0.22631-SP0
release:   11
machine:   AMD64

Python
------------------------------------------------------------------------------
implementation: CPython
sys.version: 3.12.3 (tags/v3.12.3:f6650f9, Apr  9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)]
sys.maxint: 9223372036854775807
os.getcwd(): D:\VideoKaraokePrototype\Proto2

pyglet
------------------------------------------------------------------------------
pyglet.version: 2.0.15
pyglet.compat_platform: win32
pyglet.__file__: C:\Users\Bryan Hauser\.virtualenvs\Proto2-UJiBmJLC\Lib\site-packages\pyglet\__init__.py
pyglet.options['audio'] = ('xaudio2', 'directsound', 'openal', 'pulse', 'silent')
pyglet.options['debug_font'] = False
pyglet.options['debug_gl'] = True
pyglet.options['debug_gl_trace'] = False
pyglet.options['debug_gl_trace_args'] = False
pyglet.options['debug_gl_shaders'] = False
pyglet.options['debug_graphics_batch'] = False
pyglet.options['debug_lib'] = False
pyglet.options['debug_media'] = False
pyglet.options['debug_texture'] = False
pyglet.options['debug_trace'] = False
pyglet.options['debug_trace_args'] = False
pyglet.options['debug_trace_depth'] = 1
pyglet.options['debug_trace_flush'] = True
pyglet.options['debug_win32'] = False
pyglet.options['debug_input'] = False
pyglet.options['debug_x11'] = False
pyglet.options['shadow_window'] = True
pyglet.options['vsync'] = None
pyglet.options['xsync'] = True
pyglet.options['xlib_fullscreen_override_redirect'] = False
pyglet.options['search_local_libs'] = True
pyglet.options['win32_gdi_font'] = False
pyglet.options['headless'] = False
pyglet.options['headless_device'] = 0
pyglet.options['win32_disable_shaping'] = False
pyglet.options['dw_legacy_naming'] = False
pyglet.options['win32_disable_xinput'] = False
pyglet.options['com_mta'] = False
pyglet.options['osx_alt_loop'] = False

pyglet.window
------------------------------------------------------------------------------
display: <pyglet.canvas.win32.Win32Display object at 0x00000227928F9820>
screens[0]: Win32Screen(x=0, y=0, width=5120, height=1440)
config['double_buffer'] = 1
config['stereo'] = 0
config['buffer_size'] = 32
config['aux_buffers'] = 4
config['sample_buffers'] = 0
config['samples'] = 0
config['red_size'] = 8
config['green_size'] = 8
config['blue_size'] = 8
config['alpha_size'] = 0
config['depth_size'] = 24
config['stencil_size'] = 0
config['accum_red_size'] = 16
config['accum_green_size'] = 16
config['accum_blue_size'] = 16
config['accum_alpha_size'] = 16
config['major_version'] = 3
config['minor_version'] = 3
config['forward_compatible'] = None
config['opengl_api'] = 'gl'
config['debug'] = None
context: Win32ARBContext(id=2368984375296, share=Win32Context(id=2369023726832, share=None))

window.context._info
------------------------------------------------------------------------------
gl_info.get_version(): (3, 3)
gl_info.get_vendor(): NVIDIA Corporation
gl_info.get_renderer(): NVIDIA GeForce RTX 3060/PCIe/SSE2

pyglet.gl.glx_info
------------------------------------------------------------------------------
GLX not available.

pyglet.media
------------------------------------------------------------------------------
audio driver: <pyglet.media.drivers.xaudio2.adaptation.XAudio2Driver object at 0x00000227947E9B50>

pyglet.media.ffmpeg
------------------------------------------------------------------------------
FFmpeg version: 6.1.1-full_build-www.gyan.dev

pyglet.media.drivers.openal
------------------------------------------------------------------------------
Library: <CDLL 'openal32', handle 10000000 at 0x2279c805af0>
Version: 1.1
Extensions:
   ALC_ENUMERATE_ALL_EXT
   ALC_ENUMERATION_EXT
   ALC_EXT_CAPTURE
   ALC_EXT_EFX

pyglet.input.wintab
------------------------------------------------------------------------------
WinTab: Wintab Digitizer Services 2.0 (Spec 2.0)

How To Reproduce

import pyglet

first_window = pyglet.window.Window(width = 640, height = 480)
second_window = pyglet.window.Window(width = 640, height = 480)

instructions_label = pyglet.text.Label('Hi I am a label', font_name='Times New Roman', font_size=36, x=first_window.width//2, y=first_window.height//4, anchor_x='center', anchor_y='center')
instructions_label.color = (255,255,255,255)


@first_window.event
def on_draw():
    first_window.clear()
    instructions_label.draw()


pyglet.app.run()
@ItsVeeBot ItsVeeBot added the bug Something isn't working label May 6, 2024
@benmoran56
Copy link
Member

You can only draw or interact with pyglet objects from within the same context they were created in. See this example: https://github.com/pyglet/pyglet/blob/master/examples/window/multiple_windows.py

This also applies to events that get dispatched when another context is current.

@matanox
Copy link
Contributor

matanox commented May 18, 2024

@ItsVeeBot what this means is that your code should manage the active OpenGL context that your process holds to when it is drawing or preparing elements for drawing through pyglet API. This abstract imperative translates to employing calls to Window.switch_to() such that your active OpenGL context is that of the window that you are drawing or preparing elements for drawing for.

Because there is only one OpenGL context active at any time ― you can wake up in a callback from one window, with your active OpenGL context being that of another window of your application.

Window.switch_to() switches the OpenGL context to that of the Window object that it is called on, which is how you switch to an OpenGL context to match the window that you want to draw for.

There have been some suggestions to alleviate this onus away from user code, but currently this is what you have to accomplish to avoid this exact type of crash ― analyze your flows to figure where you need to tell pyglet that the OpenGL context might need to be switched to the window that you mean to draw to ― there's always one active OpenGL context and pyglet does not guess which window's OpenGL context it should be unless you tell it.

I hope this is somewhat of a fair summary, as I'd like to cement my understanding of it before cleaning up my own multi-window application to be as minimal as possible yet sufficiently safe in setting the OpenGL context.

@matanox
Copy link
Contributor

matanox commented May 18, 2024

Maybe some API for identifying the OpenGL context (?!) could be one way for developers to scaffold this safety around their code flows rather than relying purely on theoretical analyses of their application code.

@benmoran56
Copy link
Member

pyglet.gl.current_context can be checked for seeing what the active context is, but generally speaking you should just call Window.switch_to() in order to set the context before drawing. This bare minimum example shows this in action:
https://github.com/pyglet/pyglet/blob/master/examples/window/multiple_windows.py

@matanox
Copy link
Contributor

matanox commented May 19, 2024

As a side effect of deleneating it in the above I think I have a clearer picture of how to think about my code such that I know exactly where I should switch_to in its flows of code execution.

I'll try to contribute a less minimal example of how to manage the context in the future (that example doesn't really represent any multi-window application design in any way).

@matanox
Copy link
Contributor

matanox commented May 19, 2024

Thanks a lot for suggesting pyglet.gl.current_context as an affordance in this area, I can then log the object address during development mode just to assure that I have the right one across my different dispatch contexts.

@matanox
Copy link
Contributor

matanox commented May 19, 2024

(It's probably time to remove the 'bug' label).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants