State of Accelerated OpenGL

Hm… this sounds interesting. I’ve tried this override too and it does actually work. But I didn’t get further with testing this since the Event-Loop does not work. Under Linux I can driven an event loop myself (and I do) but under Haiku I did not figure out any way so far to do an event loop like this:

while running:
  while has next message:
    process next message
  do inter-frame processing

BLooper is missing support to check for next message and processing it without blocking (all is private). Thought too about running the inter-frame processing inside an own thread but then messages would arrive in the UI thread and can not be send over to the processing thread.

What is the basic idea for handling this in Haiku? Is it even possible to have such a setup without needing two replicate event passing inside the application just to get around this limitation?

It’s a bit tricky, but possible.

In my case I put this at the end of my MessageReceived() method:

while (!IsMessageWaiting()) { /* do idle processing here */ }

It is ok to stay locked inside MessageReceived this way. Whenever there is a new message, it will exit the loop, and MessageReceived will be called again with the message. Otherwise, it stays in the loop and you can do your things there.

You can combine IsMessageWaiting and MessageReceived in other ways to achieve other effects.

Interesting albeit strange approach. But how is this working? In my first tests nothing worked. I figured out MessageReceived is called nilly willy left and right so I’m missing this “central point of message processing”.

Concrete the problem is this. I’ve got a BApplication subclass, BDirectWindow and inside a BGLView. I subclasses all to have MessageReceived forwarding to the base class MessageReceived while logging the message to get a picture of the process.

BApplication MessageReceived seems to be called never.

BDirectWindow MessageReceived seems to be called for QuitRequest and spammed with MouseIdle (what use has this message in the first place?) but everything else like resizing the window sends no message.

BGLView is also spammed with MouseIdle but does not get any mouse button, mouse move nor keyboard interaction events.

I’m puzzled by this. From reading the BeBook I understand that BApplication is BLooper subclass while all three classes are BHandler subclasses. So if I loop around inside BApplication MessageReceived how would it be possible to do this if some messages are send to a different BHandler than BApplication?

I have the impression BLooper needs a rework or better documentation for this situation.

EDIT: From looking at BLooper source code in the haiku repository I get it that the looper needs to read messages from the application port (is this the true source or am I reading something into this?). If this is the case then looping inside MessageReceived would dead-lock the application since BLooper can not read any more messages. This doesn’t sound right.

The minimal application in Haiku has two thread/loopers. There is the BApplication one, and the BWindow one.

The BApplication usually has only itself as the single BHandler. The window looper has itself for window events, plus each BView added to it.

Messages are always targetted at a specific handler, and dispatched by the looper (which will eventually call MessageReceived on the proper handler).

The messages come from a port (there is one port for each looper). IsMessageWaiting allows to poll the port for pending messages. So, as long as IsMessageWaiting returns false, you can process your own things, but when it returns true, you should exit MessageReceived and give control back to the BLooper message handling loop. This is all at the BLooper level, IsMessageWaiting will tell you if there is a pending message targetted to any of the handlers for the current looper.

For window resize, make sure your view has the B_FRAME_EVENTS flag set if you want to get them. For mouse and keyboard events, they get only to the currently focused view (BView::MakeFocus) of the currently active window, unless you use SetMouseEventMask to force-grab the mouse.

B_MOUSE_IDLE is used to make tool-tips show after a delay, apparently.

Note that this kind of “own event loop” pattern is not really the expected use of the API. Normally an application would only reply to messages, and use a periodic message (Pulse() / B_PULSE_NEEDED, or a BMessageRunner, or some other thread watching on an external event) to perform background processing.

That makes a couple of things clearer. So if I get this right then I would need to do something like this:

BApplicationSubclass::MessageReceived():
   while(!IsMessageWaiting()):
      GameEngine.FrameUpdate()
   // exit and allow looper to process message

BDirectWindow::MessageReceived():
  // no need to subclass.

Now I’m curious to know how the thread situation looks like. You said both the BApplication looper and the BWindow looper have an own thread. The Game Engine loop runs in the BApplication looper thread in this case. When now BWindow receives for example a mouse button event then this would be in a different thread. So I would need to to inter-thread-communication to handle this properly. So if for example you have an application with 3 windows then you have messages of 4 different threads fighting over data ownership, is this correct?

Yes, in that case you would have 4 threads.
To avoid problems with data ownership, the idea is that you avoid accessing data directly. In most cases, you share data by including it into BMessages which you send to other threads/loopers.

In a typical application, the view/window threads would only handle user input and rendering, and send messages to the application looper which would manage the data and perform the heavy lifting. This is what makes it possible to always have a responsive UI, even when the app is busy with something.

Of course, in some cases it makes more sense to use plain old memory sharing and appropriate locking (for example with BLocker, but also with the pthread primitives or whatever else is suitable for your use).

When I’ve got you on the rope right now some other question I could not answer looking a the documentation: Mouse handling. I need to hide the mouse cursor (that I figured out) and handle the mouse myself. I need to query the mouse position on screen and then reset it to the center of the window. The typical endless mouse handling. I found a GetMouse inside BView which claims to work for the entire screen but I could not find something like SetMouse to move the cursor. Is this actually possible in Haiku?

This way to do endless mouse handling sounds like a hack, for example it would break apart if the input device is actually a tablet or a touchscreen (both report absolute coordinates and there you can’t move the mouse to the center of the screen).

You can still do it, however, using set_mouse_position which is defined in BWindowScreen.h, but will work witohut actually using a BWindowScreen.

I tried now this new message handling but it’s still not working. Due to the way the engine is structure I’m using the following setup:

BApplicationSubclass:
   ReadyToRun():
      spawn_thread( ... a thread only calling blocking Run() on the engine )
   MessageReceived():
      send copy of message to engine using locked BMessageQueue.
      BApplication::MessageReceived

The engine checks then in the Run() loop this BMessageQueue and processes the messages it finds in the proper thread.

I’ve also used MakeFocus() ans SetMouseEventMask() on the BGLView. I only got these messages and nothing else no matter how I resized the window, pressed mouse buttons or keyboard keys:

  • ArgvReceived
  • ReadyToRun()
  • An unknown message with code “_DSA”
  • B_VIEW_RESIZED (I got this only once although I tried resizing the window multiple times)

The window does also not react to the close button. Closing the app from the tracker though sends B_CLOSE_REQUEST and B_QUIT and the app closes properly (except complaining about calling Quit() without locking the looper but I never call Quit() myself).

I’m out of ideas. This message handling in Haiku doesn’t make sense to me.

The window close button just sends a B_QUIT_REQUESTED message to the BWindow. The default behavior for this is to call BWindow::QuitRequested, to ask the window if it is ok to close it (the window may pop an alert asking the user wether to save his work or so).

By default, resizing a window does not resize the view inside it, unless you either use B_FOLLOW_ALL_SIDES in the view flags, or use the layout kit to lay out the views. Your BWindow’s MessageReceived should get FrameResized events for all window resizes, however.

That’s the strange part. In all MessageReceived methods I placed a printf showing what messages are received (ignoring the spammy MouseIdle) but these messages do not arrive. Is there something else that has to be set up for windows to produce the events? MouseIdle is spammed like hell so the message loops are working.

Just to clarify: do I have to Run() the message loop on the BDirectWindow myself too? I tried this but it segfaults so I’m not sure if I am missing something.

No, in the case of BWindow, the looper is run automatically the first time you call Show().

Then I have no idea why the message loop of BWindow does not send any messages (I had been wrong with the resize… it seems to have originated from BApplication ??)

The engine checks then in the Run() loop this BMessageQueue and processes the messages
it finds in the proper thread.

Would have been easier to keep only GameEngine::UpdateFrame() running in that “rendering” thread spawned at app start. That’s how much native Haiku OpenGL apps does currently: give a look at haiku3d/RenderView.cpp in our git repository for example.

The event handling is done as usual for any Haiku app, modifying game data (and access control via locks) when event trigger some change, which will reflect on the next frame update.

Actually, that’s a vital point. My engine outperforms others besides other design choices because it has a real separate dedicated render thread as second thread and the game logic thread is the main thread. If you put event handling into the render thread (as main thread with game logic as second thread) you loose performance.

But that’s actually not the problem here. The main message loop seems to work. BApplication does send messages but for some strange reason the BDirectWindow does not. I can only imagine I’m missing some initialization code or I’m simply not understanding something properly in the way Haiku handles messaging. The documentation in Haiku/BeOS ist quite small.

I was saying the same: have rendering done in its own thread (render thread), and handle events and game logic (data changes) in other thread, the main (wxindow’s) thread, handling both os and window events and game logic.

Regarding BDirectWindow, maybe you could check how it’s done in Haiku3D code, which use a BDirectWindow too:

I did use that file already. I also changed a few lines to match better the example (except the parts not compiling in the example) but I’m still not getting any messages from the BDirectWindow only from BApplication.

:frowning:

EDIT:
Experimented a bit more. There is actually a message coming from the BDirectWindow but only once, when I resize the window: _VRS. After this message nothing happens anymore and the rendering in the window is broken too.

EDIT:
Am I required mouse/keyboard messages to the window somehow? Do I need to acquire the LockGL() in certain situations? I do not set much extra code in the demo app but something has to be different.

BDirectWindow is a bit tricky to use. From your problem description it looks like something is locking the window and never giving back control.

The be book has a smal tutorial on using BDirectWindow: https://www.haiku-os.org/legacy-docs/bebook/BDirectWindow_Overview.html

But I would recommend using GlView in non-direct mode first (with a plain BWindow - this is how the preview for screensavers is done for example). There is of course a performance cost, but it is much simpler to do. Once you have it working this way, it should be easier to switch to BDirectWindow and see if that gives the expected performance boost.

1 Like

I tried out using BWindow instead. The result is the same.

I tried adding snooze(10000) in all threads created by myself in case I’m starving the UI threads. The results are the same.

I even tried totally disabling access to the render window (no LockGL(), no SwapBuffer(), no rendering, just doing nothing to the window at all) and still the same result.

I conclude from this it’s not my fault but that something on Haiku is broken. This is the 64-bit nightly image running in VirtualBox with Mesa Software Pipeline. Could it be this setup simply doesn’t work?