@wookie
Oh man, getting Qt to work properly in a graphics intensive application at a good frame rate is tricky!
QOpenGLWidget has absolute dog shit performance. Unfortunately you need it if you want the best integration with the rest of the Qt and nice things such as context menus. In my tests though rendering with QOpenGLWidget has an order of magnitude worse performance than simply using a QGLWidget or just QWindow. I think something really stupid is happening inside the implementations.. (texture copy or something like that perhaps..)
In general one problem is that if your setSwapInterval actually works (plenty of bugs there across the whole stack) you're blocking your thread until your swap returns. So if your display is running at 60fps then you're stuck there for 16ms at a time which delays any user input processing that much since the event loop can't run. But it gets even worse if you're rendering multiple windows/widgets! If you're swapping twice then you're blocking twice and now your frame rate is halved, so you're dropping to 30fps and the UI is sluggish as a snail!
In my game editor I've got a decent enough solution but it has taken some effort.
Essentially what I've done is:
I've settled on QWindow myself and creating the context myself and then wrapping that up inside QWidgetContainer.
I use a "busy loop" that runs and uses QApplication::processEvents directly with AllEvents | WaitMoreEvents
I use a separate thread to give a heartbeat, it sleeps some user defined interval and then posts a message to the main thread (on windows it'll try to use the compositor flush to sleep). Main thread then renders on the heartbeat message.
I disabled app level VSYNC completely (i.e setSwapInterval is always 0)
The key takeaways are that
if you use any kind of blocking such as VSYNC or thread sleep etc. in your main thread your UI janks!
if you don't use any kind of blocking and just busy loop as fast you can in your event loop you burn a lot of cycles and drain the users battery!
so whatever solution you want to create you need
ideally scaling so the frame rate is good and scales to the load
interruptible waits for throttling the main loop, react to user input as soon as possible
steady fps with good timing resolution