Single Event Loop/Thread


#1

I’ve just started to use GLFW, with only a little X11/XCB and OpenGL experience. First of all, I want to say I’ve looked through some of the code on github, and I think it’s beautifully made.

My question is about events in relation to context/window. The way I use X now, I have a single event loop in its own thread for [all] input/window events. I can create/modify windows separately at will, and it will dispatch based on the appropriate window from there very nicely.

I want to do the same thing when switching to GLFW/OpenGL. (And eventually Vulkan.)

The GLFW docs say you have to create a window first to even start the event loop. Also glfwWaitEvents/glfwPollEvents do not accept/supply a window, like in X, and the event callbacks must be set per window.

So, do I need a new thread for every glfw window? I am trying to figure out how things are normally done, and the reasons for it, not to criticize. Thank you!


#2

For cross platform compatibility GLFW needs the event processing to be done on the main thread, see this comment and thread.

Since callbacks receive the window they are for, you can then dispatch the information as needed.

You do not need to create a new thread for every window, for example see tests/windows.c where four windows are created and then rendered to in turn. If you need to thread your rendering, keep the event loop and callbacks on the main thread and place the rendering on another thread - in OpenGL only one thread can have a given context current at any time.

Does this help sufficiently?


#3

Yes, that’s a great explanation!

So, my take away is to:

  1. make all GLFW/Event calls on a dedicated thread.
  2. make another dedicated thread for GL calls [optionally.]

So, in my case, I will have to delegate to thread 1 when I need to create a window & set callbacks, as well as any window operations. (EDIT: however, this could be as trivial as posting an empty event with glfwPostEmptyEvent, so no need for extra signaling/polling.) This is because my program may request windows at any time, from any thread, as opposed to opening them at startup[*]. Then thread 1 may opt to delegate to thread 2 for rendering.

[*]The one thing that’s still a question is whether I need at least one window alive at all times while thread 1 is spinning. I’d prefer not to have to shutdown/restart thread 1 every time all windows have been closed, and then a new one is requested. I’d prefer not to create a dummy window/window pool during init either.

I thought I read in the docs that a window was required before event polling, but maybe I was mistaken, or that was outdated. (Sorry for the confusion.) I was able to find this:

GLFW needs to communicate regularly with the window system both in order to receive events and to show that the application hasn’t locked up. Event processing must be done regularly while you have any windows and is normally done each frame after buffer swapping. Even when you have no windows, event polling needs to be done in order to receive monitor connection events.

So it looks like event polling can go ahead and spin up, (and as long as glfw/events are in that thread, it’s supported cross-platform.) Did I understand that correctly?

Thanks. I apologize for the confusion.


#4

Note that thread 1 must be the main thread, i.e. the thread where the entry point main() was called.

Event polling without any windows will not crash, but may not give you any particularly useful events since most input events come via windows. I think the monitor change event is the only one you will receive when you don’t have a window.

Currently glfwWaitEvents() will always return without processing any events, as it appears to require a window. I’m not sure if this is by design, but I will raise it as it’s not documented. glfwWaitEventsTimeout should work OK by the looks.


#5

Additional note:

According to this issue on X11 glfwpostemptyevent is a no-op when no windows are present. Thus it looks like there are issues around having no windows, and I would recommend either using single a minimized window or using either your own synchronization when there are no windows (such as an condition variable or semaphore) or glfwWaitEventsTimeout with a short timeout for now.

I’ll raise this issue as the documentation and functionality here do not quite align.


#6

I’ve raised this issue: https://github.com/glfw/glfw/issues/1317


#7

Another alternative to “minimized window” is to create hidden window. Use glfwWindowHint to set GLFW_VISIBLE to GLFW_FALSE before creating window.


#8

That should work, but the thread of the issue I referenced above states there might be a bug with this on X11, though there may be a fix on the master branch.


#9

Ah ok. We’ve entered into the fun side of cross-platform compatibility :wink:

I appreciate all the support, looking into this so thoroughly. There are certainly ways to get my program to work as is. It’s just a matter of following the conventions that work across the board, such as creating a main window on the main thread, and such.

Thanks again.