PDA

View Full Version : Issues with QGLWidget as QMainWindow central widget



Zyl
10th June 2012, 22:20
Hello,

I am setting a QGLWidget as QMainWindow central widget.
I want to use it to render simple 2D GUI elements quickly.
I set it up this way:


MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QGLWidget* client = new MyQGLWidget(this);
this->setCentralWidget(client);
}

It does work, but the resulting Window has horrible resizing performance.
The content won't redraw at all unless I hold the mouse cursor still for ~100ms.

MyQGLWidget:

#ifndef MYQGLWIDGET_H
#define MYQGLWIDGET_H

#include <QGLWidget>
#include <QMouseEvent>
#include <QDebug>
#include <QPaintEvent>
#include <QSize>

class MyQGLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit MyQGLWidget(QWidget *parent = NULL);

QSizePolicy sizeHint() {
return QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}

QSize minimumSizeHint() {
return QSize(800, 600);
}

QSize maximumSize() {
return QSize(999999, 999999);
}

int minimumWidth() {
return 800;
}

int minimumHeight() {
return 600;
}

int maximumWidth() {
return 999999;
}

int maximumHeight() {
return 999999;
}

protected:
int count;

void initializeGL() {
glClearColor(0, 1, 0, 1);
glPushMatrix();
glOrtho(0, size().width(), size().height(), 0, -1, 1);
glViewport(0, 0, (GLint)size().width(), (GLint)size().height());
glDisable(GL_DEPTH_TEST);
glShadeModel(GL_FLAT);
}

void resizeGL(int w, int h)
{
glPopMatrix();
glPushMatrix();
glOrtho(0, w, h, 0, -1, 1);
glViewport(0, 0, w, h);
}

void paintGL() {
qDebug() << "paintGL() " << count;
count++;
// Draw some squares
int r = ((int) (rand())) % 255;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
glColor3f((((i * 51 + 3 + r) * (j * 127 + 17 + r)) % 173) / 173.0f,
(((i * 73 + 19 + r) * (j * 1337 + 5 + r)) % 143) / 143.0f,
(((i * 97 + 13 + r) * (j * 27 + 77 + r)) % 931) / 931.0f);
glBegin(GL_QUADS);
glVertex2i(10 + (20 * i), 10 + (20 * j));
glVertex2i(20 + (20 * i), 10 + (20 * j));
glVertex2i(20 + (20 * i), 20 + (20 * j));
glVertex2i(10 + (20 * i), 20 + (20 * j));
glEnd();
}
}
}

signals:

public slots:

};

#endif // MYQGLWIDGET_H


It does rerender properly and instantly if I do not set it as the central window and just setGeometry to large width and height,
but it won't resize according to the QMainWindow then. Is it possible to address this issue? Using Qt 4.7.4.

Thanks in advance and best regards,
Zyl

amleto
10th June 2012, 23:44
I'm guessing that using QSizePolicy::Ignored is the reason resizing is not as desired?

Zyl
11th June 2012, 00:43
Here is the whole project in its rawest form: Link (http://zyl.pestermom.com/temp/src.zip)
There may be meaningless leftover code from previous tryhardness.

wysota
11th June 2012, 11:58
This code doesn't make sense:

QSizePolicy sizeHint() {
return QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}

I don't know what you are trying to obtain with this, but sizeHint() returns QSize and not QSizePolicy and it's a const method too.

See if it helps if you call update() at the end of resizeGL().

Zyl
11th June 2012, 15:27
I don't know what you are trying to obtain with this, but sizeHint() returns QSize and not QSizePolicy and it's a const method too.
Oops! Thanks. That explains a few things. I changed the public methods to this:

public:
explicit ShardIRCClient(QWidget *parent = NULL);

QSize sizeHint() const {
return QSize(800, 600);
}

QSize minimumSizeHint() const {
return QSize(640, 480);
}
The window can now not be made smaller than a size to allow a 640*480 widget to fit, so that works properly. (QSizeHint::Preferred)



See if it helps if you call update() at the end of resizeGL().
Assuming you meant updateGL(): Yes and no. The widget now redraws when resizing it larger. It won't react if resized smaller and its overall performance is still oddly poor.

wysota
11th June 2012, 16:17
Assuming you meant updateGL(): Yes and no.
update() calls updateGL() so, no, I meant update() :)


The widget now redraws when resizing it larger. It won't react if resized smaller and its overall performance is still oddly poor.
Please provide a minimal compilable example reproducing the problem.

Zyl
11th June 2012, 17:45
This is the most minimal example I could make: Link (http://zyl.pestermom.com/temp/TestGL.zip)
Hope that shows what I mean.

wysota
11th June 2012, 18:22
I tested your app on Linux and I can't see any unexpected behaviour. When I resize the window (up or down), paintGL gets called.

As for your note regarding updateGL()... Here is what paintEvent for QGLWidget looks like (which is what gets called upon calling update()):

void QGLWidget::paintEvent(QPaintEvent *)
{
if (updatesEnabled()) {
glDraw();
updateOverlayGL();
}
}

and this is updateGL():


void QGLWidget::updateGL()
{
if (updatesEnabled())
glDraw();
}

Zyl
11th June 2012, 19:29
Strange. Using update() I get not additional paintGL()-calls, while with using updateGL() I do.
Also I already do get automatic calls to paintGL() when resizing the window, without needing to set a call to update() nor updateGL(). However, although paintGL() is definitely called, there is no change visible unless I stop moving the mouse. Only an additional updateGL() call in resizeGL() causes a visible change, BUT ONLY when resizing the window larger. It really feels as if something's running on the wrong thread, and I don't see where I'd ever have to do with that. (Win7 x64 btw)

wysota
11th June 2012, 19:44
Unless you are using threads yourself, there is no threading involved. What you observe might be somehow caused by your system settings.

Zyl
11th June 2012, 20:32
I know of no system setting regarding the rendering mechanism of OpenGL in a window.

If I remove this line from MainWindow::MainWindow

this->setCentralWidget(widget);
the problem no longer occurs. However, no layouting is performed and the widget keeps its initial size.

Doing layouting results in the whole problem returning however:

#include <QHBoxLayout>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget* center = new QWidget(this);
QGLWidget* widget = new MyGLWidget(center);
QHBoxLayout* layout = new QHBoxLayout(center);
layout->setMargin(0);
center->setLayout(layout);
center->layout()->addWidget(widget);
this->setCentralWidget(center);
}

wysota
11th June 2012, 21:16
I didn't say the system setting has to be related to rendering of OpenGL in a window. You might have some system tweak installed that causes such behaviour. If what you observe was a common problem, it would have been identified and resolved a long time ago since it's quite common to use OpenGL child windows. Does it change anything if you call center->winId() (you can ignore the result) as part of your last code snippet?

Zyl
11th June 2012, 22:23
I don't think I have any outrageous system tweaks installed. The hackiest thing on this machine is probably Dropbox with its random folder scans.
I tried adding center->winId() at various lines (and even multiple times), but there was no change.

It might be worth working with the hint that just doing

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QGLWidget* widget = new MyGLWidget(this);
widget->setGeometry(0, 0, 800, 600);
}
presents me the widget with proper performance. I can scale the window larger and as long as new parts of the 800*600 rectangle area of the widget are revealed paintGL() gets called and I see the redrawn content immediately, at what appears to be 60Hz - my screen's refresh rate; exactly what I want and one should expect. I can't help but think the problem to lie somewhere in the Qt layout classes.

wysota
11th June 2012, 22:31
I don't think I have any outrageous system tweaks installed. The hackiest thing on this machine is probably Dropbox with its random folder scans.
You'd be suprised how things tend to influence stuff logically unrelated to what the things are meant to do, especially when it comes to Windows.


I tried adding center->winId() at various lines (and even multiple times), but there was no change.
It was only needed once and in theory it shouldn't be needed at all :)


I can't help but think the problem to lie somewhere in the Qt layout classes.

Unlikely, especially since I've been using Qt+OpenGL+layouts and didn't experience any slowdowns.

As an experiment, try this:


class Widget : public QWidget {
public:
Widget(QWidget *parent = 0) : QWidget(parent) {
w = new MyGLWidget(this);
}
protected:
void resizeEvent(QResizeEvent *re) {
w->setGeometry(0,0, width(), height());
}
MyGLWidget *w;
};

int main(...) {
...
Widget w;
w.show();
...
}

No layouts, same GL widget code. Is there a slowdown? If yes, what if you replace MyGLWidget with QGLWidget?

Zyl
11th June 2012, 23:22
Same slow behaviour with MyGLWidget as well as with QGLWidget.
If this goes on I'll try making a QThread spamming sizeInfo signals about the window size and catch them in a MyGLWidget slot... >_>
EDIT: That actually works, but it acts equally crappy. (Yes I did just try it)

wysota
12th June 2012, 00:03
If this goes on I'll try making a QThread spamming sizeInfo signals about the window size and catch them in a MyGLWidget slot...
I don't see how this would help anything.

I'm still suspecting the problem is related to your system configuration. Try your program on a different machine, with a different graphics driver. It would be best if it was a vanilla installation of Windows.

Zyl
12th June 2012, 01:02
UGH! Upon you insisting it was a system problem, I tried something... and found the cause, at least.

I'm on a laptop with an "NVidia mobile GPU-equipped" graphics card GeForce GT540M and "NVidia Optimus" technology, which basically refers to some wicked mechanism which allows switching between GPU and Intel HD graphics (which is in this thing as well, apparantly). So I went to the NVidia Control Panel and set "Preferred graphics processor" from "High-performance NVIDIA processor" to "Integrated graphics", finding that this solves the original problem entirely. (Which is weird, because Integrated graphics are supposed to suck balls as opposed to GPU)

However, I still do get other strange effects, which include flickering while resizing (I was able to get a screen capture of it: Link (http://img837.imageshack.us/img837/1927/43925114.png)) and broad white stripes when resizing the window larger quickly, as if the widget wouldn't resize before redrawing. (Interestingly it appears to be impossible to get a screen capture of the later effect) I guess this is just one major NVidia (or Intel) (or Microsoft) (or all three together) fuckup. I'm not sure where the catastrophe happens, but OGL never caused bad problems on this machine.

Thanks for your help on this. If there is additional wisdom to share, please do.

wysota
12th June 2012, 01:28
Intel HD OpenGL drivers for Windows are totally broken. You should focus on getting your NVidia drivers to work properly. I'm sure the code of the program itself is correct, it works fine on my Linux+NVidia combo.

01:00.0 VGA compatible controller: NVIDIA Corporation GF104 [GeForce GTX 460] (rev a1)

norobro
12th June 2012, 01:42
If I may interject . . .

@Zyl - I see similar artifacts using your code above on my Debian box. Try calling glClear() (http://www.opengl.org/sdk/docs/man/xhtml/glClear.xml) in your
paintGL() function:
void paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
. . .

Zyl
12th June 2012, 13:23
Intel HD OpenGL drivers for Windows are totally broken. You should focus on getting your NVidia drivers to work properly. I'm sure the code of the program itself is correct, it works fine on my Linux+NVidia combo.

01:00.0 VGA compatible controller: NVIDIA Corporation GF104 [GeForce GTX 460] (rev a1)
I'd be thankful if you have any instructions/hints regarding this. I remember trying to install the NVidia driver before any Intel HD driver resulting in some message like "You must install Intel HD first [...]" or something along those lines. I also remember uninstalling Intel HD leaving me with a black screen once, requiring me to reinstall Windows - so I'm paranoid about ever touching it again. I still need to test if there are painting problems when doing updateGL() outside the content of a resize event. If yes, I won't be using OGL for anything, because there are too many people with systems like mine. If no, crappy resizing performance is an endurable pain.



If I may interject . . .

@Zyl - I see similar artifacts using your code above on my Debian box. Try calling glClear() (http://www.opengl.org/sdk/docs/man/xhtml/glClear.xml) in your
paintGL() function:
void paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
. . .
Good idea. glClear does the job. Actually... If I don't do glClear, everything still gets cleared with black. At least when I move my squares like so:

glBegin(GL_QUADS);
glVertex2i(10 + (20 * i), 10 + (20 * j) + 3 * (countPaint % 150));
glVertex2i(20 + (20 * i), 10 + (20 * j) + 3 * (countPaint % 150));
glVertex2i(20 + (20 * i), 20 + (20 * j) + 3 * (countPaint % 150));
glVertex2i(10 + (20 * i), 20 + (20 * j) + 3 * (countPaint % 150));
glEnd();
; the previously drawn ones won't stay visible in the next frame. I'm no OGL expert, but isn't this supposed to not happen unless there actually is a glClear()-call?


Another thing I noticed: I seem to be able to avoid unneccesary drawing by doing this:


void initializeGL() {
// gl-calls here

resized = true;
}

void resizeGL(int w, int h) {
// gl-calls here

resized = true;
}

void paintGL() {
if (!resized)
return;

// gl-calls to draw here...

resized = false;
}

private:
bool resized;
; this seemingly effectively increases performance and responsiveness.
EDIT: Doing this running the program using NVidia GPU however causes a black window. The squares would only flash up while resizing it.

Zyl
13th June 2012, 01:19
My research regarding NVidia Optimus resulted in finding out one's either lucky and has an option in the BIOS to disable it (I don't) or else there is no way to get rid of it, as the graphics card needs to pipe frames through the Intel HD component (or something along those lines - it got too technical for me).

I tried doing something simple as throwing 5 instances of MyQGLWidget into a QHBoxLayout, as well as into a QSplitter, both resulting in a horrendous lagfest, where every widget gets repainted one frame later than the other, regardless of whether I use GPU/Intel HD. This would mean ridiculous performance once I made a proper application from these. I then tried using traditional QWidgets using QPainter and paintEvent, which works pretty much flawlessly with either graphics component. If I just turn these into QGLWidgets again, I am back to horrible performance.

Bottom line: QWidget probably has enough performance for the simple thing I plan to do, anyway. I just cannot use GLWidgets if they're going to be unreliable, and I don't even think an NVidia Optimus-infested system such as mine already represents the worst possible target machine.

Thanks for your efforts.