PDA

View Full Version : Adding items in QGraphicsScene is slow



Grade
24th July 2013, 13:34
Hi everyone.
I'm currently making a Tiled Map Editor in Qt. I'm using QGraphicsView and it works very well. I use QGraphicsItem for every tiles of the map.
There's just one problem: Map loading is very slow.
Well it depends on the map size of course, and usually the delay isn't that high, but there's some map in my game that are huge, and for these maps, the loading delay is VERY high (around 20sec of loading). After I done some tests, I concluded that it was because of QGraphicsScene::addItem().

But after loading, the application runs correctly, QGraphicsScene isn't especially slow.
Here's the caracteristics of the biggest map currently created: 180x180 tiles. The editor works with 2 layers, so there is twice more. So the map counts 64 800 tiles. It may be a lot, but a map editor could be able to handle much more (in my opinions).

Is there some options I can turn on to fixe this delay ?
Do I have to use QGraphicsScene in a whole different way ?

Thanks in advance !

rockdemon
24th July 2013, 15:33
is the graphics scene visible when youre adding items?

Grade
24th July 2013, 16:15
What do you mean by visible ? There's no "visible" attribute in QGraphicsScene is it ? (Well I looked for it and I didn't find it).
My QGraphicsScene has default attribute.

rockdemon
24th July 2013, 16:36
i meant is the scene assigned to the view at the point you populate it?

Grade
24th July 2013, 16:40
No, it's not assigned yet. I assign it when I added all items.

wysota
24th July 2013, 17:31
Show your tile loading code.

Grade
25th July 2013, 13:53
Here's my tile loading code :

for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
ItemMap* item=new ItemMap(i+j*getW(),0,this);
item->setVal(mapLayer1[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);
}
}
for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
ItemMap* item=new ItemMap(i+j*getW(),1,this);
item->setVal(mapLayer2[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);
}
}
scenemap->setSceneRect(0,0,32*w,32*h);
cursormap=new CursorItem(this);
cursormap->setVisible(false);
scenemap->addItem(cursormap);
viewmap->setScene(scenemap);
ItemMap is a custom QGraphicsItem class (so is CursorItem). I've tried to replace ItemMap by QGraphicsPixmapItem in order to see if it's because of this class, but it's not, the loading delay is similar.

If you need any more precision, just ask. Thanks !

wysota
25th July 2013, 14:13
Try calling setItemIndexMethod(QGraphicsScene::NoIndex) before you start your loop and setItemIndexMethod(QGraphicsScene::BspTreeIndex) after you're done adding the items.

Grade
25th July 2013, 14:45
I've tried but it doesn't seem to change that much :/
Here's my new code :

scenemap->setItemIndexMethod(QGraphicsScene::NoIndex);
for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
ItemMap* item=new ItemMap(i+j*getW(),0,this);
item->setVal(mapLayer1[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);
}
}
for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
ItemMap* item=new ItemMap(i+j*getW(),1,this);
item->setVal(mapLayer2[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);
}
}
cursormap=new CursorItem(this);
cursormap->setVisible(false);
scenemap->addItem(cursormap);
scenemap->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
scenemap->setSceneRect(0,0,32*w,32*h);
viewmap->setScene(scenemap);

wysota
25th July 2013, 14:48
How much time does it take for those two lines to execute?


ItemMap* item=new ItemMap(i+j*getW(),1,this);
item->setVal(mapLayer2[i][j]);

How does it correspond to the time the whole iteration takes?


ItemMap* item=new ItemMap(i+j*getW(),1,this);
item->setVal(mapLayer2[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);

Grade
25th July 2013, 15:19
Well I can't tell how much time those two lines make to execute precisely, but if I delete this line: "scenemap->addItem(item);", It's super fast, but no graphics.

wysota
25th July 2013, 15:28
Well I can't tell how much time those two lines make to execute precisely

Use QTime::elapsed() to measure that. Rough guesses are of no use.

Grade
25th July 2013, 16:18
It returns 0 ... I don't know if I've done something wrong (I've seen some example of QTime and it seems to be right)

QTime t;
t.start();
ItemMap* item=new ItemMap(0,0,this);
item->setVal(mapLayer1[0][0]);
int elapsed=t.elapsed();
std::ostringstream imelapsed;
imelapsed << elapsed;
QMessageBox::information(this,"Elapsed time",("Time:"+imelapsed.str()).c_str());

wysota
25th July 2013, 16:56
Use qDebug() instead of showing a message box. Compare the time to the time of the whole iteration and the whole loop.

Grade
25th July 2013, 20:14
Thanks for your answer !
Here's what I did :

QTime t;
t.start();
qDebug() << "Loading started";
for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
ItemMap* item=new ItemMap(i+j*getW(),0,this);
item->setVal(mapLayer1[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);
}
}
qDebug() << "Time1:"<<t.elapsed();
for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
ItemMap* item=new ItemMap(i+j*getW(),1,this);
item->setVal(mapLayer2[i][j]);
scenemap->addItem(item);
item->setPos(i*32,j*32);
}
}
qDebug() << "Time2:"<<t.elapsed();
cursormap=new CursorItem(this);
qDebug() << "Time3:"<<t.elapsed();
cursormap->setVisible(false);
qDebug() << "Time4:"<<t.elapsed();
scenemap->addItem(cursormap);
qDebug() << "Time5:"<<t.elapsed();
scenemap->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
qDebug() << "Time6:"<<t.elapsed();
scenemap->setSceneRect(0,0,32*w,32*h);
qDebug() << "Time7:"<<t.elapsed();
viewmap->setScene(scenemap);

And here's what I got :

Loading started
Time1: 859
Time2: 1718
Time3: 1718
Time4: 1718
Time5: 1718
Time6: 14906
Time7: 14906
So I noticed that the longest delay had become this line : "scenemap->setItemIndexMethod(QGraphicsScene::BspTreeIndex);". I removed it, the loading part is now faster (around 2sec like you can see), but the editor becomes a bit laggy without this line.

wysota
26th July 2013, 01:15
Well, you are loading a lot of overlapping items. It probably might help if you changed the loading order of items so that the bsp tree generation is easier. Other than that I can suggest to load layer 2 as child items of layer1 (as opposed to siblings), maybe that will make indexing faster.

Grade
26th July 2013, 18:57
Thanks for your advice !
I've tried, but it's actually slower (1sec more).

Here's my code :

for (int i=0 ; i<getW() ; i++) {
for (int j=0 ; j<getH() ; j++) {
int num=i+j*getW(),xt=i*32,yt=j*32;
ItemMap* item=new ItemMap(num,0,this);
item->setVal(mapLayer1[i][j]);
item->setPos(xt,yt);
ItemMap* item2=new ItemMap(num,1,this);
item2->setVal(mapLayer2[i][j]);
item2->setParentItem(item);
scenemap->addItem(item);
}
}
I'm begining to think that there's no alternative. I find that weird though. Sure there's a lot of items but waiting 14sec for a map is kind of long...

Thanks for helping me !

wysota
26th July 2013, 19:06
You're scene is not that large to load 20 seconds.

Could you show us the code for ItemMap class?

Edit: on my computer the following code takes ~500ms to load


#include <QtGui>

int main(int argc, char **argv) {
QApplication app(argc, argv);
QGraphicsScene scene;
QGraphicsView view;
QTime t;
t.start();
const int w = 256;
for(int i=0;i<180;++i) {
for(int j=0;j<180;++j) {
QGraphicsRectItem *item = new QGraphicsRectItem;
item->setRect(QRectF(0,0,w,w));
item->setBrush(QColor(QColor::colorNames().at(qrand() % 24)));
scene.addItem(item);
item->setPos(i*w, j*w);
}
}
scene.setSceneRect(0,0,180*w, 180*w);
view.setScene(&scene);
view.show();
qDebug() << t.elapsed() << "ms";
return app.exec();
}

Grade
26th July 2013, 23:48
Thanks for helping me !
itemmap.h:

#ifndef ITEMMAP_H
#define ITEMMAP_H

#include <QGraphicsItem>
#include "fenetre.h"

class ItemMap : public QGraphicsItem
{
public:
ItemMap(int n, int c, Fenetre* parent=0);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
void setVal(int i);
int getVal();
int getCouche();

private:
Fenetre* fenetre;
int val;
int num;
int couche;

};

#endif // ITEMMAP_H
itemmap.cpp

#include <QPainter>
#include <QPixmap>
#include "itemmap.h"

ItemMap::ItemMap(int n, int c, Fenetre *parent) :
QGraphicsItem(), fenetre(parent), val(0), num(n), couche(c)
{
setAcceptHoverEvents(true);
}

QRectF ItemMap::boundingRect() const
{
return QRectF(0, 0,
32, 32);
}

void ItemMap::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
if (couche==1 && fenetre->getCouche()==0) { painter->setOpacity(0.5); }
painter->drawPixmap(0,0,32,32,fenetre->getPixset()->copy((val%fenetre->getMaxChip())*32,(int)(val/fenetre->getMaxChip())*32,32,32));
if (couche==0 && fenetre->getCouche()==1) {
painter->setOpacity(0.5);
painter->fillRect(QRectF(0,0,32,32),QBrush(Qt::black));
}
}

void ItemMap::setVal(int i)
{ val=i; }

int ItemMap::getVal()
{ return val; }

int ItemMap::getCouche()
{ return couche; }



EDIT: I've tried your code and ... well I guess my pc must be really slow because it takes 10 000 secs to execute ...

ChrisW67
27th July 2013, 00:53
3 Hours! Wow! What is the elapsed time measured between lines 19 and 20?

Grade
27th July 2013, 01:05
I'm sorry for the mistake, I meant 10 000ms of course (=10 sec, what I was about to say at first). But that's a lot compared to 500ms.
I mesured what you asked for, and it tells me that there's 0ms between those lines (It must be too fast to detect it). So I think the problem here is adding items.
I should ask a friend to test my application for me. Because I don't care if it's slow for me, really, I just want it to be fast for the others. (Well it would be better if it's fast for everybody but ...)

Well if you have more ideas, don't hesitate, Thanks!

ChrisW67
27th July 2013, 02:01
Sorry, I meant the elapsed time at line 20 since line line 8. Trying to determine if it is the scene building (before line 20) or the displaying (line 20 onward) that is taking the longest.

Which compiler, Qt version and Windows version? Are you using an ANGLE or OpenGL build?

wysota
27th July 2013, 09:13
Sorry, I meant the elapsed time at line 20 since line line 8. Trying to determine if it is the scene building (before line 20) or the displaying (line 20 onward) that is taking the longest.
Previous tests seem to indicate it is indexing the scene that takes that time. Which could suggest Grade is using a really old version of Qt

The fun part is that with Qt5 (linux, xcb) the same code takes almost a second to execute on my computer.

Grade
27th July 2013, 15:09
One second ! Wow that's a huge difference indeed.

About my Qt version, I downloaded it recently so I'm under Qt 5.0.2. My OS is Windows XP Pro SP3 and I've got an Intel Core 2 Duo (CPU).
I don't know what do you mean by an ANGLE or an OpenGL Build (I've searched for it but I didn't find anything), I didn't change any configuration so every build setting is the default one.