Russian Qt Forum
Апрель 25, 2024, 23:36 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: QPixmap vs. QImage  (Прочитано 18574 раз)
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?
Записан
once_again_abc
Гость
« Ответ #1 : Сентябрь 02, 2011, 09:34 »

простейший тест показал, что рисование 500000 через image.setPixel + pPixmap->convertFromImage _значительно_ быстрее чем рисование 350000 через painter.drawPoint.

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

пс. тему неправильно назвал - не QPixmap vs. QImage, а QPainter vs. QImage
Записан
GreatSnake
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2921



Просмотр профиля
« Ответ #2 : Сентябрь 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
« Последнее редактирование: Сентябрь 02, 2011, 10:51 от GreatSnake » Записан

Qt 5.11/4.8.7 (X11/Win)
once_again_abc
Гость
« Ответ #3 : Сентябрь 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-ы, имхо, следует только в случае статических изображений.

спасибо!
Записан
once_again_abc
Гость
« Ответ #4 : Сентябрь 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() );
}

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

Записан
GreatSnake
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2921



Просмотр профиля
« Ответ #5 : Сентябрь 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 );
}
« Последнее редактирование: Сентябрь 02, 2011, 13:04 от GreatSnake » Записан

Qt 5.11/4.8.7 (X11/Win)
once_again_abc
Гость
« Ответ #6 : Сентябрь 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 я забыл =)
Записан
once_again_abc
Гость
« Ответ #7 : Сентябрь 02, 2011, 11:01 »

Еще один глупый вопрос. Правильно ли я понимаю, что никаких утечек памяти в коде ниже нет?

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

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

Сообщений: 2921



Просмотр профиля
« Ответ #8 : Сентябрь 02, 2011, 11:07 »

А какие здесь могут быть утечки, если делается простое копирование Непонимающий
Записан

Qt 5.11/4.8.7 (X11/Win)
once_again_abc
Гость
« Ответ #9 : Сентябрь 02, 2011, 11:15 »

А какие здесь могут быть утечки, если делается простое копирование Непонимающий

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Сентябрь 02, 2011, 11:21 »

Непонятно с синхронизацией - как paintEvent (которое может прийти в любой момент) узнает что mIimage обновлен? И что делать если еще нет?  

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

Спасибо
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #11 : Сентябрь 02, 2011, 11:32 »

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

Гугль в помощь
once_again_abc
Гость
« Ответ #12 : Сентябрь 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() );
}


и никаких проблем с синхронизайицей. проверено на практике. скорость отрисовки полумиллиона точек просто аццкая =)))
Записан
SASA
Гость
« Ответ #13 : Сентябрь 02, 2011, 12:01 »

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

Отдельный поток владеет двумя пиксмапами, один из которых использует для текущего рисования, а другой пиксмап с отрисованными данными используется ГУИ потоком для отображения.
А как вы рисуете в потоке? И если не рисуете - зачем потоки?

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


Страница сгенерирована за 0.167 секунд. Запросов: 22.