PDA

View Full Version : Optimize QGraphicsItem update



paolom
21st March 2011, 10:17
Hi, I want to show a video with QGraphicsView and QGraphicsItem.

I need to show every 40 ms a QImage. I use a QTimeLine and a QGraphicsItem.

The problem is that when I call the update() on the QTimeLine slot, there is a lot of time ( about 10 ms ) between the update() call and the paint event of the item.

I know that there isn't an equivalent repaint() for the QGraphicsItem, but anyone know a way to reduce this time ??

Thanks

wysota
21st March 2011, 10:52
The time depends on what your application is doing apart showing this one item. Without knowing any details it is not possible to suggest an optimization.

paolom
21st March 2011, 10:57
The application is doing anything. I try to use QApplication::processEvents() after the update() call but it doesn't change nothing.

Any ideas?

wysota
21st March 2011, 11:04
Please show us some code (especially the part where you set the frames on the item). ProcessEvents will not help because the application will be processing events anyway. Also please state where you get the frames from.

paolom
21st March 2011, 11:05
This is the code:




/* The QTimeLine slot called every 40 ms */
void VideoItem::goToFrame(int frameNumber)
{
m_currentFrameNumber = frameNumber;
updateFrame();
}

void VideoItem::updateFrame()
{
if ( m_file )
{
QImage im;
bool finished;
if ( ( m_currentFrameNumber - m_previousFrameNumber ) == 1 )
{
im = m_file->getNextFrame(finished);
}
else
{
for ( int i = 0 ; i < ( m_currentFrameNumber - ( m_previousFrameNumber + 1 ) ) ; i++ )
m_file->getNextFrame(finished,false);

if ( !finished )
im = m_file->getNextFrame(finished);

}

m_previousFrameNumber = m_currentFrameNumber;

if ( !im.isNull() && !finished)
{
m_counter++;
m_frameCount = m_currentFrameNumber;

setCurrentImage(im);
}
else
{

stop();

emit videoFinished();
}
}
}

void VideoItem::setCurrentImage(QImage im)
{
m_currentImage = im;
update();
}

wysota
21st March 2011, 11:07
What is m_file and how does its getNextFrame work? Does it load an image from a file? Also, how large is each frame?

paolom
21st March 2011, 11:14
getNextFrame() gets a frame from an mpeg-2 video using the ffmpeg. I've tested() the time for this method and it spends few ms. The bottleneck is between the update() call from setCurrentImage() and the really paint time. For this time tests I've use QElapsedTimer.

getNextFrame() gets a frame from an mpeg-2 video using the ffmpeg. I've tested() the time for this method and it spends few ms. The bottleneck is between the update() call from setCurrentImage() and the really paint time. For this time tests I've use QElapsedTimer.

Added after 4 minutes:

This is the painter code:



void VideoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setRenderHint(QPainter::SmoothPixmapTransform,fals e);
painter->setRenderHint(QPainter::HighQualityAntialiasing,fa lse);
painter->setRenderHint(QPainter::NonCosmeticDefaultPen,fals e);
painter->drawImage(this->boundingRect(),currentImage());
}

QRectF VideoItem::boundingRect() const
{
return QRectF(0,0,720,576);
}

wysota
21st March 2011, 11:22
The delay you experience is probably caused by some other events being processed first and additionally by the need to setup the item transformation by graphics view architecture. There are some optimizations you can do but you need to find the true bottleneck. QElapsedTimer will not give you reliable result because it is influenced by the environment. It measures a time span and not the time used by the process. If scheduler doesn't schedule your process for some time, this time will be included in QElapsedTimer measurements. Show me the paint() routine of your item, tell me the number of elements the scene contains and any custom flags you have set on any of the items or custom settings of the scene. And the platform you are running on.

MarekR22
21st March 2011, 11:39
I'm not sure for 100%, but I think paint event is synchronized with screen refresh (in LCD displays it is usually 60Hz).
At least during of some testing I note that paint was call not more often then refresh rate of the monitor.
So this may be source of delay between update call and paint event.
I would check how Qt code solves this kind of problems (QMove, QLabel, QSvgRenderer).

paolom
21st March 2011, 11:45
Running on Mac Os X 10.6.6.

The scene contains one item which is the base item (QGraphicsRectItem ) with any custom flags ( it has the default flags).

This base item has a child item (called GridItem which is a QGraphicsRectItem ) with this flags ( other than the default flags ):



setFlag(QGraphicsItem::ItemClipsChildrenToShape,tr ue);
setFlag(QGraphicsItem::ItemIsFocusable,true);


The gridItem has a child item ( called ImageItem which is QGraphicsObject ) with this flags ( other than the default flags ) :



setFlag(QGraphicsItem::ItemIsSelectable,false);
setFlag(QGraphicsItem::ItemIsFocusable,true);


The ImageItem has a ChildItem which is the VideoItem ( QGraphicsObject) which has any flags other than the default flags.

The scene has not any custom settings.



void VideoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setRenderHint(QPainter::SmoothPixmapTransform,fals e);
painter->setRenderHint(QPainter::HighQualityAntialiasing,fa lse);
painter->setRenderHint(QPainter::NonCosmeticDefaultPen,fals e);
painter->drawImage(this->boundingRect(),currentImage());
}


/* QGraphicsView option */
View::View(QGraphicsScene *scene, MPWindow * win, MPGridBoxItem * box, QWidget *parent)
:QGraphicsView(scene,parent),
_window(win),
_box(box)
{

setRenderHint(QPainter::SmoothPixmapTransform,fals e);
setRenderHint(QPainter::HighQualityAntialiasing,fa lse);
setRenderHint(QPainter::NonCosmeticDefaultPen,true );

QRectF rect = scene->sceneRect();
setSizePolicy(QSizePolicy::Expanding,QSizePolicy:: Expanding);
resize(rect.width() ,rect.height());

setAlignment(Qt::AlignLeft);
QBrush brush(QColor(Qt::black));
brush.setStyle(Qt::SolidPattern);
setBackgroundBrush(brush);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff) ;
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOf f);
}

wysota
21st March 2011, 15:29
Why do you need children clipping? It slows down things quite significantly. Furthermore activate OpenGL viewport for your view, this should make rendering faster. I would also advise to use QtConcurrent-based thread to prefetch frames from the video, especially if you're targetting multi-core machines.