What is the fastest way to draw a circle ?
Hello All !
I'm using QGraphicsView to visualize a huge amount of particles which are basically circles with fixed size (about 14x14px). The particles can have non-integer coordinates so I'm using antialiasing (without it the scene looks just horrible). When the number of particles is around 200 performance becomes very poor. I've tried drawing using two different methods:
Code:
painter->setPen(Qt::NoPen);
painter->setBrush(Qt::black);
painter
->drawEllipse
(QRectF(-radius,
-radius,radius
*2,radius
*2));
and
Code:
painter
->setPen
(QPen(Qt
::black, radius
));
painter->drawPoint(0,0);
Both gives approx. the same performance. I've also tried drawing in-advance into QPixmap, but then I can't place that pixmap at non-integer position and there is very noticeable jitter when particle moves.
Are there any suggestions on how to improve drawing performance ? Thanks in advance !
Re: What is the fastest way to draw a circle ?
Could you provide a minimal compilable example reproducing the problem?
Re: What is the fastest way to draw a circle ?
Yes, I've prepared an example. Running it on my (not very old) PC I'm getting around 10 FPS. Are there any suggestions how to improve performance ?
Code:
#include <QtGui>
#include <QtCore>
const int N = 1000;
const int S = 500;
const int DT = 10;
const double R = 7;
{
public:
MyGraphicsItem(): _vel((double(qrand())/RAND_MAX-0.5)*S/10,(double(qrand())/RAND_MAX-0.5)*S/10),
_rect
(-R,
-R,
2*R,
2*R
), _color
(QColor::fromRgba(qrand
())) { setPos(double(qrand())/RAND_MAX*S,double(qrand())/RAND_MAX*S);
}
painter
->setRenderHint
(QPainter::Antialiasing,
true);
painter->setPen(Qt::NoPen);
painter->setBrush(_color);
painter->drawEllipse(_rect);
}
QRectF boundingRect
() const { return _rect;
} void step() {
setPos(pos() + _vel*double(DT)/1000);
if(pos().x() < 0 || pos().x() > S) _vel.setX(-_vel.x());
if(pos().y() < 0 || pos().y() > S) _vel.setY(-_vel.y());
}
protected:
};
{
public:
MyGraphicsScene
(): _startTime
(QTime::currentTime()), _framesCount
(0) { for(int i=0; i<N; ++i) addItem(new MyGraphicsItem);
startTimer(0);
}
protected:
static_cast<MyGraphicsItem*>(item)->step();
++_framesCount;
if(_framesCount == 50) {
qDebug
() <<
"FPS:" <<
double(_framesCount
) / _startTime.
msecsTo(QTime::currentTime()) * 1000;
_startTime
= QTime::currentTime();
_framesCount = 0;
}
}
int _framesCount;
};
int main(int argc, char *argv[])
{
MyGraphicsScene scene;
view.resize(S,S);
view.scale(0.95,0.95);
view.show();
return app.exec();
}
Re: What is the fastest way to draw a circle ?
The first thing I would try is to use OpenGL viewport instead of a regular one (call setViewport(new QGLWidget)) on the view).
Re: What is the fastest way to draw a circle ?
With OpenGL I'm getting 17 fps but with worse rendering quality.
Re: What is the fastest way to draw a circle ?
Removing QApplication:: processEvents improved FPS by 10 on mys system. I don't think you need to call that since you are not using an infinite loop.
Re: What is the fastest way to draw a circle ?
Try passing QGLFormat(QGL::SampleBuffers) as the first argument to the GLWidget constructor. As far as I remember this should allow antialiasing to work. Also get rid of the scene scaling and change the timer interval from 0 to 50 and see if you can achieve 20fps with it. I seem to have worse hardware than you but I managed to go up to 15fps with it. You could also use the pixel buffer of your graphics card to render the circles. Hopefully it would be faster this way, but you'd probably have to reimplement your paint method to use GL calls (I'm not sure of that though). See the pixel buffer example for details. Maybe you could use a single texture that contains a cirle and just render it differently.
Another thing to try is to render each circle into a pixmap and use drawPixmap() to render the item instead of drawing the ellipse each time.
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
Gopala Krishna
Removing QApplication:: processEvents improved FPS by 10 on mys system. I don't think you need to call that since you are not using an infinite loop.
Yes, removing it improves performance, but for some strange reason without it MyGraphicsItem::paint() is called only once per two frames !
Re: What is the fastest way to draw a circle ?
I guess Wysota is correct in recommending item caching in pixmap. Actually i tried andreas's new item caching patch with your program and with some modifications to your program i could get ~30 FPS. With processEvents i could get ~20FPS.
Here are the changes i made
- Firstly i applied the patch specified
- Changed update mode to FullViewportMode
- Removed scaling
- Enabled item's dynamic cache(available only through patch)
Code:
#include <QtGui>
#include <QtCore>
const int N = 1000;
const int S = 500;
const int DT = 10;
const double R = 7;
{
public:
MyGraphicsItem(): _vel((double(qrand())/RAND_MAX-0.5)*S/10,(double(qrand())/RAND_MAX-0.5)*S/10),
_rect
(-R,
-R,
2*R,
2*R
), _color
(QColor::fromRgba(qrand
())) {
setPos(double(qrand())/RAND_MAX*S,double(qrand())/RAND_MAX*S);
}
painter
->setRenderHint
(QPainter::Antialiasing,
true);
painter->setPen(Qt::NoPen);
painter->setBrush(_color);
painter->drawEllipse(_rect);
}
QRectF boundingRect
() const { return _rect;
} void step() {
setPos(pos() + (_vel*double(DT)/1000));
if(pos().x() < 0 || pos().x() > S) _vel.setX(-_vel.x());
if(pos().y() < 0 || pos().y() > S) _vel.setY(-_vel.y());
}
protected:
};
{
public:
MyGraphicsScene
(): _startTime
(QTime::currentTime()), _framesCount
(0) { setItemIndexMethod(NoIndex);
for(int i=0; i<N; ++i) {
MyGraphicsItem *it = new MyGraphicsItem();
addItem(it);
}
startTimer(0);
}
protected:
static_cast<MyGraphicsItem*>(item)->step();
}
++_framesCount;
if(_framesCount == 50) {
qDebug
() <<
"FPS:" <<
double(_framesCount
) / _startTime.
msecsTo(QTime::currentTime()) * 1000;
_startTime
= QTime::currentTime();
_framesCount = 0;
}
}
int _framesCount;
};
int main(int argc, char *argv[])
{
MyGraphicsScene scene;
view.resize(S,S);
view.show();
return app.exec();
}
EDIT: Forgot to inform you about my hardware. I have descent hardware with 1 GB ram, 3 GHz processor and an ATI Radeon XPress 200m card.
1 Attachment(s)
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
wysota
Try passing QGLFormat(QGL::SampleBuffers) as the first argument to the GLWidget constructor. As far as I remember this should allow antialiasing to work.
Thanks, it really works !
Quote:
Originally Posted by
wysota
Also get rid of the scene scaling and change the timer interval from 0 to 50 and see if you can achieve 20fps with it. I seem to have worse hardware than you but I managed to go up to 15fps with it. You could also use the pixel buffer of your graphics card to render the circles. Hopefully it would be faster this way, but you'd probably have to reimplement your paint method to use GL calls (I'm not sure of that though). See the pixel buffer example for details. Maybe you could use a single texture that contains a cirle and just render it differently.
Another thing to try is to render each circle into a pixmap and use drawPixmap() to render the item instead of drawing the ellipse each time.
I've tried different combinations of it and here is my results:
using drawPixmap (but this gives jitter!):
without GL, without scaling: 25 fps
without GL, scaling: 4 fps (in all other cases scaling does not changes anything)
with GL: 18 fps
using drawEllipse:
without GL: 11 fps
with GL: 17 fps
using drawPoint and big pen:
without GL, without scaling: 9 fps
without GL, without scaling: 7 fps
with GL, without scaling: 13 fps
with GL, with scaling: 12 fps
using drawRect (which is not what I want):
without GL: 15 fps
with GL: 25 fps
EDIT: My hardware is Athlon X2 3800+, 1GB ram, GeForce FX 5500. The code I've used in this benchmark is in the attached file.
So I'll probably stick with using GL. But what if user has not OpenGL (I've tried to run my program in Xephyr and got just error messages). Are there any ways to check it at runtime ?
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
Gopala Krishna
I guess Wysota is correct in recommending item caching in pixmap. Actually i tried
andreas's new item caching patch with your program and with some modifications to your program i could get ~30 FPS. With processEvents i could get ~20FPS.
Thanks for the advice ! But when using QPixmap I'm getting very noticeable jitter when particle moves because QPixmap can only be drawn at integer position. Are this problem fixed with the patch you mentioned ?
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
Vladimir
So I'll probably stick with using GL. But what if user has not OpenGL (I've tried to run my program in Xephyr and got just error messages). Are there any ways to check it at runtime ?
QGLFormat::hasOpenGL()
QGLFormat::sampleBuffers()
Quote:
Originally Posted by
Vladimir
Thanks for the advice ! But when using QPixmap I'm getting very noticeable jitter when particle moves because QPixmap can only be drawn at integer position. Are this problem fixed with the patch you mentioned ?
You can speed up pixmap scaling by tweaking transformation modes from smooth to fast. You can improve quality by doing the opposite thing. You'll always have aliasing when using pixmaps, because they are pixel based (using smooth transform mode will reduce the effect). But if you use GL pixel buffers and render the circle to texture, you should avoid any artifacts at all. Should be very simple in your case.
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
Vladimir
Thanks for the advice ! But when using QPixmap I'm getting very noticeable jitter when particle moves because QPixmap can only be drawn at integer position. Are this problem fixed with the patch you mentioned ?
By jitter you mean shaky movement right ? I did get them when using the caching mechanism. But anyway I think pixmap can be drawn in real positions too -
Quote:
void QPainter::drawPixmap ( const QRectF & target, const QPixmap & pixmap, const QRectF & source )
I found this prototype in assistant.
With both caching and gl i could get 20 FPS. (your card is better than mine ;) )
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
Gopala Krishna
I found this prototype in assistant.
I've tried this but it still gives the same result.
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
wysota
QGLFormat::hasOpenGL()
QGLFormat::sampleBuffers()
Thanks !
Quote:
Originally Posted by
wysota
You can speed up pixmap scaling by tweaking transformation modes from smooth to fast. You can improve quality by doing the opposite thing.
I still can't figure out how to do it. I can draw into QPixmap with antialiasing but the problem is that when I'm calling QPainter::drawPixmap (even without any transformations) the pixmap is always painted starting from some pixel on the screen, for example when position of the item is (15.4,19.7) it will still be drawn at position (15,20). When items are slowly moving it gives the impression that the movement is shaky.
Re: What is the fastest way to draw a circle ?
Well... you can't draw in the middle of a pixel. If you scale up the painter, the precision should increase.
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
wysota
Well... you can't draw in the middle of a pixel. If you scale up the painter, the precision should increase.
I could suppose that QT can resample my pixmap using algorithm similar to one using for resizing...
Anyway, for now I've decided to use OpenGL which should be available in 90% cases with fallback to slow software rendering in other cases. On old machines without OpenGL even QPixmap approach can be too slow so I'll probably provide advances options to draw particles as points instead of circles.
1 Attachment(s)
Re: What is the fastest way to draw a circle ?
Here is something to play with. Some parts of the code are commented and uncommenting some of them might change the behavior a bit.
The most important part is the one that makes sure the animation is "lag-undependent" - items will move properly regardless of the number of frames the hardware can handle.
Re: What is the fastest way to draw a circle ?
Quote:
Originally Posted by
wysota
Here is something to play with. Some parts of the code are commented and uncommenting some of them might change the behavior a bit.
The most important part is the one that makes sure the animation is "lag-undependent" - items will move properly regardless of the number of frames the hardware can handle.
Thanks ! GL version gives 25fps for me. In my case position calculation is more complicated: each frame is calculated in separate thread and calculation sometimes can be long. However frame time is adjustable so I'll try to follow your ideas and adjust it when rendering takes more time than calculations.