Lazy Qt rendering on OpenGL

I came to this problem and knew that this could be done better.

Problem:

When you overlay a QGLWidget (Qt OpenGL contextview) on Qt widgets, Qt redraws these widgets after each Qt frame.

Qt is not designed to constantly redraw all windows s> 60fps, so it is extremely slow.

My idea:

Make Qt use something else to draw: a transparent texture. Create OpenGL using this texture when it redraws and draws on top of everything else. Make Qt redirect all interactions with the OpenGL context to widgets drawn on textures.

The advantage would be that Qt only needs to redraw when necessary (for example, the widget freezes or clicks, or the text cursor in the text field blinks), and can perform partial redrawing, which is faster.

My question is:

How to approach this? how can i tell qt to draw a texture? how can I redirect the interaction with widgets to another (for example, if I move the mouse over the area in the context view where the flag is in the widgets of the drawn figure, Qt should register this event in the flag and redraw its state to reflect)

+7
source share
1 answer

I separate 2D and 3D rendering from my CAD-like application for the same reasons as you, although in my case I am not a widget, but this should not change. Here's how to approach the problem:

  • When your widget changes display it on a QGLFramebufferObject , do it using FBO as a QPaintDevice for QPainter in QGLWidget::paintEvent(..) and calling myWidget->render( myQPainter, ...) . Repeat this for any number of widgets that you have, but only on the same FBO - do not create FBOs for each of them ... Remember to clear it first as a "normal" framebuffer.
  • When your current OpenGL background changes, render it to another QGLFramebufferObject using standard OpenGL calls in the same way.
  • Create a passage through the vertex shader (the "camera" will be just a cube of one) and a very simple fragment shader that can stack two textures on top of each other.
  • At the end of QGLWidget::paintEvent(..) activate your shader program, bind the framebuffers as textures for it ( myFBO->texture() gets a handle) and draw a square of one. Since your camera is a unit square, and the viewport size determines the size of the FBO, it perfectly fills the viewport pixel.

However, the easy part ... The hard part is the interaction of the widget. Since you essentially represent a “proxy”, you will have to pass the interaction between the “real” and “proxy” widgets, while preserving the invisible “real” widget. Here's how I would start:

  • Some operating systems are a little strange in visualizing widgets without even displaying them, so you may need to show and then hide the widget after creating the instance - due to the smart drawing queue in Qt, this is unlikely to really appear on the screen.
  • Catch all mouse events in the viewport, work out a “proxy” widget over which the cursor ended (if any), and then move it to get the relative position for the “real” hidden widget - this value will depend on what the parent object has view of the "real" widget. Then pass the event to the “real” widget before redrawing the widget framebuffer.

I must point out that I also had to create a “tagging” system to draw the redraw well. You do not want each widget event to trigger a FBO widget redraw because there could be many simultaneous events (not just thinking about the mouse), but you only need one redraw. Therefore, I created a system in which, if something in the application can visually visualize something in the viewport, then the flag will be visible as "dirty". Then configure a QTimer for any number of frames that you are aiming for (in my situation, the scene can become very difficult, so I also calculated how long the frame takes, and then used this value + 10% as a timer delay for the next check, so thus, the system is not bombarded when rendering becomes lagging). And then check the dirty status: if it's dirty, redraw; otherwise no. I found life easier with two dirty flags, one for 3D material and one for 2D, but if you need to maintain a constant drawing speed for an OpenGL drawing, maybe there is no need for two.

I imagine what I did, it was not the easiest way to do this, but it provides many options for customization and profiling - which makes life easier in the long run. All the answers are certainly not in this post, but hopefully it will help you in choosing a strategy.

+5
source

All Articles