Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: once_again_abc от Сентябрь 02, 2011, 09:03



Название: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 09:03
Задача обычная - рисовать попиксельно кучу данных в реальном времени в виде графиков, с поддержкой увеличения/уменьшения масштаба.
Для решения этой задачи я выбрал следующий путь.

Отдельный поток владеет двумя пиксмапами, один из которых использует для текущего рисования, а другой пиксмап с отрисованными данными используется ГУИ потоком для отображения. После очередного этапа рисования пиксмапы меняются и ГУИ отображает новые данные. Сейчас у меня наблюдаеца эффект мерцания, что может быть вызвано рассинхронизацией данных в пиксмапах (я не аккуратно рисую, чисто в качестве теста). Меня волнует вопрос производительности - правильно ли я сделал, что выбрал такой подход вообще и QPixmap для попиксельного рисования в частности (вместо QImage)?

Пытаюсь анализировать все что известно об этих классах и вот что получается (согласно документации):

- QPixmap.drawPoint медленнее чем QImage.setPixel
- QPixmap.drawPoint на стороне сервера, QImage.setPixel на стороне клиента
- Защищенный swap( ptr1, ptr2 ) быстрее QPixmap.fromImage

получается, что QImage пикселы рисовать быстрее, но при этом преобразование в QPixmap очень медленное.
Так что опять не могу решить вопрос - что быстрее: 1) рисовать пикселы в QImage, а затем ковертировать их в QPixmap с отправкой на сервер или 2) Рисовать сразу на стороне сервера в QPixmap вроде бы медленными drawPoint?

Поделитесь пожалуйста опытом и направьте на путь истинный =)
Спасибо!

Что быстрее: 500000 drawPoint или 500000 setPixel + один fromImage?


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 09:34
простейший тест показал, что рисование 500000 через image.setPixel + pPixmap->convertFromImage _значительно_ быстрее чем рисование 350000 через painter.drawPoint.

так что считаю, что вопрос решен в пользу QImage пока кто-то не докажет иное =)

пс. тему неправильно назвал - не QPixmap vs. QImage, а QPainter vs. QImage


Название: Re: QPixmap vs. QImage
Отправлено: GreatSnake от Сентябрь 02, 2011, 09:43
Здесь целая куча ньюансов:
* QPixmap хранится на стороне Xserver-a в случае X11 и на стороне GDI в случае винды
* QImage хранится на стороне клиента
* при любом композинге в QPixmap-е на стороне клиента требуется гнать его содержимое из Xserver/GDI в клиент и потом обратно
* рендеринг в QImage обычно делается силами CPU, хотя может и можно как-то используя GPU
* в Qt по-умолчанию включен backing-store - все QWidget::paintEvent()'s сначала отрисовываюся в double-buffer QImage и уже после этого изменённая часть double-buffer-а копируется непосредственно в окно.

Исходя из всего этого для твоих целей оптимальнее всего изпользовать QImage и вообще не использовать QPixmap. В paintEvent() делать копирование из готового QImage.
Использовать QPixmap-ы, имхо, следует только в случае статических изображений.

Рекомендуется к обязательному прочтению Qt Graphics and Performance (http://labs.qt.nokia.com/2009/12/16/qt-graphics-and-performance-an-overview/)


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 09:50
Здесь целая куча ньюансов:
* QPixmap хранится на стороне Xserver-a в случае X11 и на стороне GDI в случае винды
* QImage хранится на стороне клиента
* при любом композинге в QPixmap-е на стороне клиента требуется гнать его содержимое из Xserver/GDI в клиент и потом обратно
* рендеринг в QImage обычно делается силами CPU, хотя может и можно как-то используя GPU
* в Qt по-умолчанию включен backing-store - все QWidget::paintEvent()'s сначала отрисовываюся в double-buffer QImage и уже после этого изменённая часть double-buffer-а копируется непосредственно в окно.

Исходя из всего этого для твоих целей оптимальнее всего изпользовать QImage и вообще не использовать QPixmap. В paintEvent() делать копирование из готового QImage.
Использовать QPixmap-ы, имхо, следует только в случае статических изображений.

спасибо!


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 10:31
Исходя из всего этого для твоих целей оптимальнее всего изпользовать QImage и вообще не использовать QPixmap. В paintEvent() делать копирование из готового QImage.

тогда еще такой вопрос. Оптимален ли такой подход:

Код:
class workerThread: public QThread { 
    ...

    QImage    m_Image;
    QPixmap   m_Pixmap;
    QMutex     m_Mutex;

    void run( void );

    QPixmap& GetPixmap( void ) const {
        QMutexLocker( m_Mutex );
        return &m_Pixmap;
    }

    ...
}

void workerThread:::run( void ) {

    while( 1 ) {

        // Do drawing onto m_Image

        // Upon finishing translate
        m_Mutex.lock();
        m_Pixmap.convertFromImage( m_Image );
        m_Mutex.unlock();
    }
}


void myWidget::paintEvent(...) {
    ...
    painter.drawPixmap( 0, 0, m_pThread->GetPixmap() );
}

Покритикуйте этот псевдокод пожалуйста. Можно ли сделать проще, быстрее и красивее?



Название: Re: QPixmap vs. QImage
Отправлено: GreatSnake от Сентябрь 02, 2011, 10:42
Не вижу вообще смысла в использовании QPixmap.

Код
C++ (Qt)
class workerThread: public QThread {
   ...
   QImage    m_Image;
   QMutex     m_Mutex;
 
   void run( void );
 
   QImage GetImage( void ) const {
       QMutexLocker( m_Mutex );
       return m_Image;
   }
 
   ...
}
 
void workerThread:::run( void ) {
 
   while( 1 ) {
 
       // Do drawing onto m_Image
 
       // Upon finishing translate
       emit imageDone();
   }
}
 
void myWidget::slotImageDone()
{
   m_Image = m_pThread->GetImage();
   update();
}
 
void myWidget::paintEvent(...) {
   ...
   painter.drawImage( 0, 0, m_Image );
}


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 10:55
Не вижу вообще смысла в использовании QPixmap.

Код
C++ (Qt)
class workerThread: public QThread {
   ...
   QImage    m_Image;
   QMutex     m_Mutex;
 
   void run( void );
 
   QImage GetImage( void ) const {
       QMutexLocker( m_Mutex );
       return m_Image;
   }
 
   ...
}
 
void workerThread:::run( void ) {
 
   while( 1 ) {
 
       // Do drawing onto m_Image
 
       // Upon finishing translate
       emit imageDone();
   }
}
 
void myWidget::slotImageDone()
{
   m_Image = m_pThread->GetImage();
}
 
void myWidget::paintEvent(...) {
   ...
   painter.drawImage( 0, 0, m_Image );
}

действительно, про drawImage я забыл =)


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 11:01
Еще один глупый вопрос. Правильно ли я понимаю, что никаких утечек памяти в коде ниже нет?

Код
C++ (Qt)
 
void myWidget::slotImageDone()
{
   m_Image = m_pThread->GetImage();
}
 
}

GetImage вернет копию объекта, который при последующем GetImage будет правильно уничтожен благодаря механизму подсчета ссылок?


Название: Re: QPixmap vs. QImage
Отправлено: GreatSnake от Сентябрь 02, 2011, 11:07
А какие здесь могут быть утечки, если делается простое копирование ???


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 11:15
А какие здесь могут быть утечки, если делается простое копирование ???

нет, утечек не может быть. это я на всякий случай спросил =) просто хотел уточнить, какое именно копирование происходит.

скажем, есть статический объект, которому периодически присваиваются другие объекты. как реализован оператор = для QImage? предположу, что создается новый объект с непустым счетчиком ссылок, а у старого объекта счетчик ссылок обнуляется и объект удаляется. не происходит же прямое копирование "значение в значение"?


Название: Re: QPixmap vs. QImage
Отправлено: Igors от Сентябрь 02, 2011, 11:21
Непонятно с синхронизацией - как paintEvent (которое может прийти в любой момент) узнает что mIimage обновлен? И что делать если еще нет?  

По поводу QPainter/QPixmap. Если возиться с пикселями/коипозицией то все ясно - нужно использовать QImage. Но мне непонятно а что с др "рисованием" - в первую очередь с выводом текста и с clip регионами. В случае QPixmap этим занимается OC, но что в случае если QImage используется как QPaintDevice? Qt рисует вместо ОС? Это непросто и может быть не быстро.

Спасибо


Название: Re: QPixmap vs. QImage
Отправлено: navrocky от Сентябрь 02, 2011, 11:32
По поводу QPainter/QPixmap. Если возиться с пикселями/коипозицией то все ясно - нужно использовать QImage. Но мне непонятно а что с др "рисованием" - в первую очередь с выводом текста и с clip регионами. В случае QPixmap этим занимается OC, но что в случае если QImage используется как QPaintDevice? Qt рисует вместо ОС? Это непросто и может быть не быстро.
Да, в этом случае используется встроенный растровый движок. Но не факт, что это не быстро. Это может быть быстрее, т.к. в случае пиксмапа используется слишком жирная прослойка в виде иксов.


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 11:33
Непонятно с синхронизацией - как paintEvent (которое может прийти в любой момент) узнает что mIimage обновлен? И что делать если еще нет?  

По поводу QPainter/QPixmap. Если возиться с пикселями/коипозицией то все ясно - нужно использовать QImage. Но мне непонятно а что с др "рисованием" - в первую очередь с выводом текста и с clip регионами. В случае QPixmap этим занимается OC, но что в случае если QImage используется как QPaintDevice? Qt рисует вместо ОС? Это непросто и может быть не быстро.

Спасибо

либо я не понял вопроса, либо мне кажется ответ очевидным:

дополнительно к слоту (принудительное дерганье paintEvent) имеем:
Код:
void QPlotter::paintEvent( QPaintEvent * pEvent ) {

    QPainter painter( this );

    painter.drawPixmap( 0, 0, m_pThread->GetPixmap() );
}


и никаких проблем с синхронизайицей. проверено на практике. скорость отрисовки полумиллиона точек просто аццкая =)))


Название: Re: QPixmap vs. QImage
Отправлено: SASA от Сентябрь 02, 2011, 12:01
Отдельный поток владеет двумя пиксмапами, один из которых использует для текущего рисования, а другой пиксмап с отрисованными данными используется ГУИ потоком для отображения.
А как вы рисуете в потоке? И если не рисуете - зачем потоки?


Название: Re: QPixmap vs. QImage
Отправлено: once_again_abc от Сентябрь 02, 2011, 12:36
Отдельный поток владеет двумя пиксмапами, один из которых использует для текущего рисования, а другой пиксмап с отрисованными данными используется ГУИ потоком для отображения.
А как вы рисуете в потоке? И если не рисуете - зачем потоки?

как обычно через QPainter/QPixmap. две пиксмапы для того, чтобы в одной рисовать, а другую отдать потоку ГУИ для отображения. когда цикл отрисовки закончился - посменять пиксмапы местами, если первая пиксмапа еще не занята ГУИ, иначе ждем когда ГУИ закончит рисование и тогда уже меняем. и так далее в бесконечном цикле.
но это было раньше =)
сейчас все намного проще и лушче - как обсуждается в этом топике.


Название: Re: QPixmap vs. QImage
Отправлено: GreatSnake от Сентябрь 02, 2011, 12:56
Непонятно с синхронизацией - как paintEvent (которое может прийти в любой момент) узнает что mIimage обновлен? И что делать если еще нет?  
m_Image - текущее результирующее изображение, предназначенное для отображения. Отображается таким какое есть. В каждом потоке своё изображение, которое копируется в результирующее изображение по мере готовности. paintEvent() не должен знать про степень готовности m_Image. Имея в каждом потоке свой m_Image можно не беспокоиться насчёт синхронизации :)


Название: Re: QPixmap vs. QImage
Отправлено: Igors от Сентябрь 02, 2011, 14:36
m_Image - текущее результирующее изображение, предназначенное для отображения. Отображается таким какое есть. В каждом потоке своё изображение, которое копируется в результирующее изображение по мере готовности. paintEvent() не должен знать про степень готовности m_Image. Имея в каждом потоке свой m_Image можно не беспокоиться насчёт синхронизации :)
Хорошо, пусть "что-то изменилось", и нужно показать новый имедж. Каким образом Вы скажете нитке (рисующей в QPixmap или QImage) что нужно заняться рисованием? И как остановить ее когда отрисовано? Не вижу этого в приведенных фрагментах кода.

Ладно, допустим как-то сказали и вызвали update. Но ведь paintEvent может прийти раньше чем нитка займется рисованием - она вернет старый имедж. Откуда возьмете следующий update?  Так что про "степень готовности" главная нитка должна знать, иначе какие-то обновления не будут отображаться.

Да, в этом случае используется встроенный растровый движок. Но не факт, что это не быстро. Это может быть быстрее, т.к. в случае пиксмапа используется слишком жирная прослойка в виде иксов.
Ладно, будет минутка - проверю  :)



Название: Re: QPixmap vs. QImage
Отправлено: Авварон от Сентябрь 02, 2011, 15:45
Вопрос к размышлению - зачем туи мьютексы?
Присваивание (то есть свап) кутешных шаред классов - атомарная операция, насколько мне известно.


Название: Re: QPixmap vs. QImage
Отправлено: Igors от Сентябрь 02, 2011, 19:06
Вопрос к размышлению - зачем туи мьютексы?
Присваивание (то есть свап) кутешных шаред классов - атомарная операция, насколько мне известно.
Это дает только что N ниток могут одновременно читать (и расшаривать) такую переменную - но не спасает от crash если запись и чтение выполняются параллельно.

Поизучал маленько (аттач). Результат на OSX меня удивил:

- прямой вывод на экран - менее 1 сек
- вывод в QPixmap а потом на экран - 12 сек
- вывод в QImage потом на экран - тоже примерно 12 сек

Отключал финальный вывод (drawImage, drawPixmap), ну минус пол-секунды. Профайлер показывает что во всех случаях используется одна и та же Qt Engine. Вывод текста позорно медленный, все съедается на извлечении glyph(ов) - вернее на защищающей эту операцию блокировке.

Еще одной неожиданностью оказалось что repaint вовсе не перерисовывает немедленно. Похоже что он засылает в очередь сообщение UpdateRequest (не путать с update). В любом случае repaint не зовет paintEvent сам, нужен processEvents 

Возможно это "только текст" и "только OSX" - у меня нет оснований обобщать


Название: Re: QPixmap vs. QImage
Отправлено: nike1987 от Сентябрь 14, 2011, 18:40
У меня наверное схожий вопрос автора, необходимо загружать на сцену(QGraphicsScene) довольно-таки увесистые изображения и периодически обновлять их.Сей час работает схема Qimage.load + Qpixmap.convertFromImage.Возможно ли ускорить как-то добавление изображения на сцену?Для добавления использую стандартную функцию сцены addPixmap().