AUG
26
2008

How to get faster Qt painting on N810 right now

My previous post touched on the horrid FPS you can expect from any graphics intensive Qt app on the N810 at the moment. Ariya has pointed out one reason for the bad numbers: Qt decides to convert all 16 bit pixmaps to 32 bit before blitting even if the source QPaintDevice and the destination QPaintDevice are both 16 bit.

Well, here is a workaround I found while we wait for some pretty big changes in the Qt painting engine for 4.5 that Ariya hinted at. Mind you it is a big hack, but it seems to work well for my use case and perhaps it would be useful to others.

Introducing QX11RasterWidget:


#include

class QX11RasterWidget : public QWidget {
public:
QX11RasterWidget(QWidget *parent = 0, Qt::WindowFlags f = 0);
virtual ~QX11RasterWidget();

QImage *rasterDevice() const;

protected:
virtual bool event(QEvent *event);
virtual void paintEvent(QPaintEvent *event);
virtual void resizeEvent(QResizeEvent *event);

private:
void flushToX11();

private:
QImage *m_rasterDevice;
};


#include "qx11rasterwidget.h"

#include
#include
#include

#include
#include
#include

QX11RasterWidget::QX11RasterWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_OpaquePaintEvent, true);

m_rasterDevice = 0;
}

QX11RasterWidget::~QX11RasterWidget()
{
delete m_rasterDevice;
}

QImage *QX11RasterWidget::rasterDevice() const
{
return m_rasterDevice;
}

bool QX11RasterWidget::event(QEvent *event)
{
if (m_rasterDevice && event->type() == QEvent::Paint) {
//Make sure all paint operations redirect here
QPainter::setRedirected(this, m_rasterDevice);
bool accept = QWidget::event(event);
flushToX11();
return accept;
}
return QWidget::event(event);
}

void QX11RasterWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
}

void QX11RasterWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);

delete m_rasterDevice;
m_rasterDevice = new QImage(event->size(), QImage::Format_RGB16);
}

void QX11RasterWidget::flushToX11()
{
if (!m_rasterDevice) {
return;
}

//Flush the m_rasterDevice to the X11 window
Display *display = QX11Info::display();
Visual *visual = (Visual*)x11Info().visual();
Drawable hd = handle();
int w = m_rasterDevice->width();
int h = m_rasterDevice->height();
int depth = x11Info().depth();

XImage *xi = XCreateImage(display, visual, depth, ZPixmap, 0,
reinterpret_cast(m_rasterDevice->bits()), w, h, 32,
m_rasterDevice->bytesPerLine());

GC gc = XCreateGC(display, hd, 0, 0);
XPutImage(display, hd, gc, xi, 0, 0, 0, 0, w, h);
XFreeGC(display, gc);
XFlush(display);
}



The technique is straightforward. In QX11RasterWidget::event(...) we redirect all paint operations of the widget to the QImage m_rasterDevice. And then when the paint operations are done, we directly put the image to the X11 window by using XPutImage. This has obvious drawbacks, but will result in nice increase in FPS. You can forget about child widgets though. This does not do any composition management of child widgets. It will also not likely work if you want a remote connection to your X app. But it does make things faster.