Crash using QPainter from secondary thread
-
Hello, I'm trying to draw into a QImage using a QPainter from a secondary thread. As far as I know this should be supported by Qt.
I get a consistent crash (segfault) deep inside the paint engine, possibly sse related. In the code below, it crashes inside the call to painter.drawLine().
I'm running XP using Qt 4.7.3 with mingw and QtCreator 2.1.0I've managed to produce a small sample which exhibits the issue, could anyone take a look and see if I'm doing anything stupid, or even if you can just reproduce it (it doesn't seem to occur on my vista machine).
========== MainWindow.h =========
@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QtGui/QMainWindow>
#include <QThread>
#include <QWidget>
#include <QImage>class Canvas : public QWidget
{
Q_OBJECT
public:
Canvas(QWidget* parent=0);void drawStuff();
protected:
void paintEvent(QPaintEvent *);QImage m_image;
};class RenderThread : public QThread
{
public:
RenderThread(Canvas* canvas) : m_canvas(canvas), m_stopping(false) {}void run();
void stop() { m_stopping = true; }private:
Canvas* m_canvas;
bool m_stopping;
};class MainWindow : public QMainWindow
{
Q_OBJECTpublic:
MainWindow(QWidget *parent = 0);
~MainWindow();private:
RenderThread* m_renderThread;
};#endif // MAINWINDOW_H
=========== MainWindow.cpp ============
#include "MainWindow.h"
#include <QPainter>
#include <QTimer>
#include <QMutex>QMutex g_mutex;
Canvas::Canvas(QWidget *parent)
: QWidget(parent),
m_image(256,256, QImage::Format_ARGB32_Premultiplied)
{
}void Canvas::drawStuff()
{
g_mutex.lock();m_image.fill(0xffffffff);
QPainter painter(&m_image);
QPen pen(QColor(40,90,180,200));
pen.setWidthF(12.0f);
painter.setPen(pen);
painter.drawLine(QPointF(0,0), QPointF(256,256));g_mutex.unlock();
}void Canvas::paintEvent(QPaintEvent *)
{
g_mutex.lock();QPainter p(this);
p.drawImage(0,0, m_image);g_mutex.unlock();
}void RenderThread::run()
{
while(!m_stopping)
{
m_canvas->drawStuff();
msleep(10);
}
}MainWindow::MainWindow(QWidget parent)
: QMainWindow(parent)
{
Canvas canvas = new Canvas(this);
setCentralWidget(canvas);m_renderThread = new RenderThread(canvas);
m_renderThread->start();
}MainWindow::~MainWindow()
{
m_renderThread->stop();
m_renderThread->wait();delete m_renderThread;
}
@EDIT: please use @-tags for code highlighting, thanks. Gerolf
-
As far as I remember, QImage: no multithread support, QPixmap: multithread support.
-
Franzk,
it should be the other way round:
bq. Because QImage is a QPaintDevice subclass, QPainter can be used to draw directly onto images. When using QPainter on a QImage, the painting can be performed in another thread than the current GUI thread.
-
Well... do you get any messages in the application output about the painter? You could try to do the m_image.fill in the constructor maybe? As soon as you create your canvas, I think it generates a paintEvent() or atleast on the setCentralWidget().. and so maybe drawing the m_image with uninitialized data is the problem.. maybe..
-
There are no messages in the output, and moving the image.fill doesn't seem to make any difference.
Since it seems to be explicitly supported in the docs, I'm wondering if it's something to do with my local setup, can anyone else reproduce it? -
I can reproduce it with win XP and mingw...
-
Using MSVS2008 and Qt 4.7.0, it works, using Qt 4.7.3 and mingw, it crashes....
-
Will try to repro..
[Edit: Ubuntu 10.04 Qt SDK 1.1.1 Qt 4.7.4.. works.. no crash.. ]
-
Ah, thanks. That's interesting.
-
Sorry, one mistake, I used 4.7.2.
Mingw --> crash, msvs2008 --> works
4.7.0 msvs2008 --> worksvery strange...
-
Does this sound like something that should be reported on the Qt bugtracker? If so, where do I do that?
-
"For info on how to post a bug....":http://developer.qt.nokia.com/wiki/ReportingBugsInQt
-
[quote author="bourbon" date="1308069402"]Does this sound like something that should be reported on the Qt bugtracker? If so, where do I do that?[/quote]
It does to me. Sounds like a regression, and those are generally taken quite seriously. Don't forget to attach your example to reproduce the issue, link to this topic, and add a tag to this topic with the bug id (QTBUG-#####)
-
The first thing that comes in my mind is that you're not end()ing the painting before releasing the lock, thus you're relying on the QPainter dtor (which will end the painting itself).
There's an obvious race condition there, and I'm confident that helgrind would have told you so. Try adding end() there, or switch to QMutexLocker.
-
The latter will probably give cleaner code.
-
The crash occurs in the very first call to painter.drawLine(), so I don't think it's a race condition.
I didn't realise I shold be calling end(), I've added it but it makes no difference (the code never reaches it). -
I have posted the root cause, a small project to reproduce it and a temporary workaround in the Bugtracker for this bug: QTBUG-19886
It is caused by a stack mis-alignment of the newly created thread. It affects the drawing engine which expects 16-byte alignment for the used SSE2 instructions.
It appears to only occur on windows XP. And happens when a QPainter::drawImage is done on a different thread. The stack on the new thread (QThread subclass and started with start()) is NOT 16-bit aligned, and causes a Segmentation Fault on
the MMX instruction PANDN in qdrawhelper_sse2.cpp.The only (dirty) workaround I can think of is to manually push extra bytes on the stack in case it is not aligned to realign the SP to a 16-byte boundary. And this seems to work quite well, no more crashes!