Russian Qt Forum

Qt => Дополнительные компоненты => Тема начата: carhun от Октябрь 31, 2012, 06:00



Название: [qwt] Производительность при перерисовки графика
Отправлено: carhun от Октябрь 31, 2012, 06:00
Добрый день.

Есть следующая проблема:
У меня есть набор QwtPlot'ов, их к-во может сколько угодно, но допустим сейчас их у меня до 100 штук. На каждом из этих QwtPlot'ов может быть по 1-5 кривых и у каждой кривой получается в среднем по 200 000 точек, бывает и под миллион. Собственно, и тогда возникает проблема, что когда я допустим хочу прокрутить все графики, то приходится вызвать setAxisScale(QwtPlot::xBottom, min, max), к-ый за собой потянет вызов replot(), к-ый естственно будет выполнятся ну очень с большими лагами для такого к-во точек и графиков....

Пробывал делать через QwtPlotPanner он тоже не сильно помог, пока дергаешь график туда-сюда оно хорошо не глючит, а когда отпускаешь график, так я понял, там опять вызвается replot() и все таже беда...

Собственно, какие есть способы улучшить производительность? Слишком накладно получается каждый раз вызывать replot() ... Ну или может я вообще ошибаюсь и не в replot'e дело...


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: mutineer от Октябрь 31, 2012, 10:29
Может стоит уменьшить количество точек, по которым рисуется кривая? 200000 на экране все равно не видно


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: carhun от Октябрь 31, 2012, 11:30
На сколько я понял qwt сама отрисовует только нужную ей часть, у меня точки отобраны так что бы они шли раз в 3 пикселя, а дальше я все это скармилваю ф-ии setSamples() для кривой. и на сколько я понимаю она дальше отрисовует только ту часть, что я указываю при axisScaleX(...).


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Декабрь 17, 2013, 19:49
Подниму вопрос.

Допустим, в плоте у кривой отображается большое кол-во данных, 1000000, например. Далее, приходит новая порция размером в 100000. Необходимо, сдвинуть кривую, т.е. эти новые данные отобразить слева, а самые старые данные (те, что справа) выкинуть. Если не рассматривать искусственное уменьшение данных, то каким образом достичь максимального быстродействия?

Если поразмышлять, то кривая могла бы делать кэш (QPixmap), далее отсекать от него фрагмент, соответствующий сбрасываемым данным, и добавлять фрагмент, соответствующий новым данным. Реализовано ли такое поведение в Qwt?


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: popper от Декабрь 18, 2013, 12:08
Посмотри QwtPlotCurve::setRawSamples


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Декабрь 18, 2013, 15:39
Это не то.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Igors от Декабрь 19, 2013, 11:18
Если поразмышлять, то кривая могла бы делать кэш (QPixmap), далее отсекать от него фрагмент, соответствующий сбрасываемым данным, и добавлять фрагмент, соответствующий новым данным. Реализовано ли такое поведение в Qwt?
Никогда не пользовался Qwt, просто добавлю еще одно размышление/соображение. Нет уверенности/гарантии что такое оптимизированное рисование будет "бесшовным" и равным полному.  Именно за счет анти-алиаса. Зная натуру Qt (приоритет макс удобство, а не производительность) - вполне возможно что и нет. И это можно проверить перекрыв paintEvent


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Декабрь 20, 2013, 09:19
Да. В интерфейсе кривой мелькает флаг антиалиаса, но я думаю, что разработчик учитывал такие моменты.

Поставлю еще один вопрос: отображается кривая в 1000000 точек. Далее, я меняю точки в диапазоне [100000, 200000]. Если я буду использовать QwtSeriesStore<QPointF>::setData(...), то заново установлю все 1000000 точкек, соответственно, будет полная медленная перерисовка (медлительность возникает за счет расчетов по отображению 1000000 точек на текущий размер канваса плота). Мне нужно, чтобы был произведен перерасчет именно этого интервала и отрисовался измененный ограничивающий прямоугольник.

Я вижу
Код:
virtual void QwtPlotSeriesItem::drawSeries 	(	QPainter * 	painter, 
const QwtScaleMap & xMap,
const QwtScaleMap & yMap,
const QRectF & canvasRect,
int from,
int to
) const
которая, похоже, делает то, что мне нужно. Но я не хочу QPainter'ов в интерфейсе. Я хочу нечто в духе updateData(from, to).


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Декабрь 20, 2013, 10:04
По последнему вопросу сейчас вижу такое решение:
1. Делаю наследника QwtSeriesData<> с тем, чтобы предоставлять для Qwt доступ к внешнему буферу, содержащему 1000000 точек.
2. Изменяю в буфере необходимый фрагмент. Qwt об этом ничего не знает.
3. Для перерисовки обновленных данных использую QwtPlotSeriesItem::drawSeries(). Требуемые функции параметры (canvasRect и т.п.) вычисляю.

Тем, кто в теме, просьба скорректировать мой взгляд, если есть лучший вариант. Qwt выглядит продуманной либой, и меня смущает отсутствие простого интерфейса в духе updateData(from, to). Возможно, я что-то упускаю. Спасибо.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Январь 08, 2014, 10:10
Если перерисовка занимает много времени, например, 250 мс в моем случае много, то как-то хотелось бы "разбавить" код перерисовки в Qwt вызовом пользовательского коллбэка, который мог бы вызывать QCoreApplication::processEvents(), например.

Никто не озадачивался подобным?


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Racheengel от Январь 08, 2014, 21:32
Мы когда-то решали подобную проблемы. Qwt тупит и не оптимизирует отрисовку как следует. Все решилось имплементацией собственного рисования с отфильтровкой накладывающихся точек в памяти.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Январь 09, 2014, 19:06
Спасибо за ответ. Qwt предлагает достаточно широкие возможности отображения (линии, стемы, и т.д., интерполяция, опять же), и мне может потребоваться любая такая фича. На собственное рисование у меня нет времени, но как вариант это отмечу.



Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Igors от Январь 09, 2014, 19:41
Если перерисовка занимает много времени, например, 250 мс в моем случае много, то как-то хотелось бы "разбавить" код перерисовки в Qwt вызовом пользовательского коллбэка, который мог бы вызывать QCoreApplication::processEvents(), например.

Никто не озадачивался подобным?
Озадачивался подобным - рисование может быть долгим. Проблема в том что processEvents может быть опасным, напр вызвать рекурсивный paintEvent или изменить данные рисования. Часто встречающаяся рекомендация типа "рисуй в pixMap" при ближайшем рассмотрении ничего не решает, к тому же заметно тормозит. Я ограничился прерыванием: в рисовании периодически проверяю нажата ли клава или мышь, если да - выход с update окна что вызовет след перерисовку.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Январь 09, 2014, 20:26
Ок. Спасибо. Да, бездумный вызов processEvent() действительно может быть опасным.

Мне, например, нужно перерисовывать некий виджет с интервалом 100 мс. (анимация). Но у меня есть есть отрисовка в 250 мс...


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Old от Январь 09, 2014, 20:26
Если перерисовка занимает много времени, например, 250 мс в моем случае много, то как-то хотелось бы "разбавить" код перерисовки в Qwt вызовом пользовательского коллбэка, который мог бы вызывать QCoreApplication::processEvents(), например.

Никто не озадачивался подобным?
Лучше рендерить долгую графику в отдельном потоке в теневой буфер и обновлять окно, только когда оно будет полностью сформировано.

Я ограничился прерыванием: в рисовании периодически проверяю нажата ли клава или мышь, если да - выход с update окна что вызовет след перерисовку.
Ага, только никто не гарантирует, что скопившиеся события обработаются быстро. :)
Нарисовали половину кадра, пользователь нажал мышь, вышли из рисовалки, пользователь увидел на экране половину картинки, а ваша программа ушла обрабатывать события... и делала это секунду (ну обработку одного из событий делал специалистпрофессионал) и только после этого мы вернулись и дорисовали кадр. Пользователь будет очень грустить, рассматривая не дорисованные кадры или совместно часть старого и часть нового кадра.
Все таки, пока кадр не готов полностью, показывать его не стоит.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Racheengel от Январь 10, 2014, 01:03
Лучше рендерить долгую графику в отдельном потоке в теневой буфер и обновлять окно, только когда оно будет полностью сформировано.

а еще лучше тогда параллельно в нескольких потоках... смотря сколько ядер на целевой машине, конечно...
Но имхо лучшее решение - просто математические выкинуть ненужные точки перед рендером.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Январь 10, 2014, 10:45
Qwt 6 (как минимум) выкидывает ненужные точки (есть такая настройка). Может она делает это не самым быстрым образом? У меня точек сотни тысяч/единицы миллионов, поэтому даже сам расчет получается долгим, я полагаю.

Я даже не ставлю сейчас цели оптимизировать рисование (хотя это было бы здорово), а просто прерывать его пользовательским коллбэком. Т.е., через каждые, скажем, 10000 точек Qwt будет звать внешний коллбэк.

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

 


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Igors от Январь 10, 2014, 11:33
Все таки, пока кадр не готов полностью, показывать его не стоит.
Этой проблемы нет  (double-buffer)


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Old от Январь 10, 2014, 11:57
Все таки, пока кадр не готов полностью, показывать его не стоит.
Этой проблемы нет  (double-buffer)
Вы выходите из paintEvent и не происходит обновление окна?
Покажите код вашего обработчика и назовите платформу.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Igors от Январь 10, 2014, 12:45
Вы выходите из paintEvent и не происходит обновление окна?
Покажите код вашего обработчика и назовите платформу.
Когда прерывание обнаружено - экран еще НЕ обновлен, и я могу решить что с ним делать.

Ага, только никто не гарантирует, что скопившиеся события обработаются быстро. :)
Нарисовали половину кадра, пользователь нажал мышь, вышли из рисовалки, пользователь увидел на экране половину картинки, а ваша программа ушла обрабатывать события... и делала это секунду (ну обработку одного из событий делал специалистпрофессионал)
Ну что же Вы выдвигаете такие "абсурдные" требования? (как Вы сами давеча говорили в др теме) :) Это где ж видано чтобы рисовалось а события были запрещены? Вот напр Qt Dnd таких ограничений не имеет. И.т.д.  :)

и только после этого мы вернулись и дорисовали кадр.
Ну "дорисовать" - это вряд ли, напр OpenGL рисование не инкрементально. Придется начинать все с нуля.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Old от Январь 10, 2014, 13:15
что же Вы выдвигаете такие "абсурдные" требования? (как Вы сами давеча говорили в др теме) :) Это где ж видано чтобы рисовалось а события были запрещены?
А в чем абсурдность и причем запрещенность событий?
Смотрите, вы вышли из paintEvent, управление вернулось в цикл обработки событий и сработал таймер, в обработчике которого мы полезли проверять обновления на сайт и длилось это действо секунду. Потом мы вернулись в цикл и перешли опять к рисованию. Вот и задержка между началом и продолжением рисования.
А что получили - непонятно? Рисовать все равно начали заново. :)
Как я понимаю если пользователь будет клацать мышкой, программа может вообще никогда ничего не нарисовать. :)


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Igors от Январь 10, 2014, 14:00
Как я понимаю если пользователь будет клацать мышкой, программа может вообще никогда ничего не нарисовать. :)
Это нормально, пусть меньше клацает :) Другое дело если операция интерактивна, обычно когда пользователь перемещает объекты мышью. Тогда нужно разрешать лишь небольшое число событий.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Old от Январь 10, 2014, 14:10
Нормально? Ну не знаю, у нас разные представления о "нормально".


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Январь 12, 2014, 21:07
1. Я тут сделал следующую пробную реализацию: перекрыл QwtPlotCurve::drawSeries() с целью вызывать пользовательский коллбэк через каждые 10000 точек. Коллбэк делает следующее:
Код:
QCoreApplication::sendPostedEvents(&processingObject_, QEvent::MetaCall);
т.е. вызывает слоты некоего processingObject_, которые на данный момент имеются в очереди сообщений. Никакие другие события в очереди сообщений не обрабатываются. Вызванный т.о. слот processingObject_ в свою очередь может вызвать синхронную перерисовку другого виджета (repaint()), например, находящегося ниже по лейауту относительно QwtPlot.

Так вот, работает это несколько некорректно - не полностью или вовсе не отрисовываются другие виджеты главного окна. Я не стал разбираться. Полагаю, что причина может быть в следующем: Кьют (в моем случае) использует двойную буферизацию (backingStore) при отрисовке виджетов, а также не использует нативные контролы, т.е. сам рисует виджеты. Когда от системы приходит событие перерисовки (свернули/развернули окно, например), то если содержимое виджета не меняется, то и QWidget::paintEvent() по идее звать не нужно - в систему пойдут данные из кэша. А вот если содержимое виджета меняется (например, QLabel::setText("...")), то необходимо еще и кэш обновить. Т.е. QWidget::paintEvent() должно вызываться из контекста, связанного с этой синхронизацией. И прямой вызов repaint() одного виджета из paintEvent() другого не имеет этого контекста (не реентерабельный контекст). Но это просто поверхностное рассуждение, если кто занимался вопросом, просьба прояснить ситуацию.

2. Несколько копнул Qwt 6 на предмет отрисовки.
Например, имеется 120000 точек, шкала по оси абсцисс установлена так, что отображается только первые 10000 точек. Что делает Qwt при измененнии кривой и репаинте:
а) вне зависимости от шкалы на отрисовку спускает все 120000 точек.
б) уменьшает кол-во точек, т.к. при отрисовке большого кол-ва точек некоторые будут просто проецироваться в один и тот же пиксель. Но! При этом она чешет все 120000 вместо того, чтобы "прощупать", где начинаются видимые точки.
в) полученные точки перед отрисовкой клиппируются, т.е. в полилинии остаются те, которые соответствуют видимым 10000.
г) паинтер рисует полилинию.

Вообще, архитектура Qwt совсем небыстрая, я бы сказал в стиле Java, а не С++. Например, исключительно часто вызываемая функция QwtSeriesData::sample() виртуальная. Это как если бы в STL у нас бы был базовый интерфейс итератора с виртуальными функциями доступа к данным :) Это удобно, но гораздо медленнее.


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Igors от Январь 13, 2014, 12:47
Так вот, работает это несколько некорректно - не полностью или вовсе не отрисовываются другие виджеты главного окна. Я не стал разбираться. Полагаю, что причина может быть в следующем: Кьют (в моем случае) использует двойную буферизацию (backingStore) при отрисовке виджетов, а также не использует нативные контролы, т.е. сам рисует виджеты. Когда от системы приходит событие перерисовки (свернули/развернули окно, например), то если содержимое виджета не меняется, то и QWidget::paintEvent() по идее звать не нужно - в систему пойдут данные из кэша. А вот если содержимое виджета меняется (например, QLabel::setText("...")), то необходимо еще и кэш обновить. Т.е. QWidget::paintEvent() должно вызываться из контекста, связанного с этой синхронизацией. И прямой вызов repaint() одного виджета из paintEvent() другого не имеет этого контекста (не реентерабельный контекст). Но это просто поверхностное рассуждение, если кто занимался вопросом, просьба прояснить ситуацию.
Сейчас у меня несколько похожие проблемы. Что мне удалось выяснить

1) Qt обеспечивает double-buffer там где ОС его не имеет (Вындоуз), иначе используется буфер ОС  (OSX). В любом случае QPainter рисует в буфере, в чем легко убедиться поставив break в paintEvent - никаких изменений на экране еще нет. Выталкивание буфера на экран происходит по окончании paint.  Важно что вызов repaint НЕ гарантирует немедленной перерисовки на всех платформах. При этом QApplication::flush никак не поможет, т.к. внутренний буфер еще не обновлен (paintEvent не было)

2) Qt часто вызывает/провоцирует paint сам вызовом update. Напр при переключении активного окна оба (новое и старое) будут перерисованы. То же самое при свертке/развертке. Избавиться от этого можно, но с трудом. Вот если таскаем окно - да, нижние не будут перерисовываться, срабатывает буфер.

3)
а также не использует нативные контролы, т.е. сам рисует виджеты
Это не одно и то же. На OSX каждому Qt контролу соответствует нативный контрол OC (т.е. используются), но рисуются они не ОС'ом, а самим Qt 

Возвращаясь к Вашим проблемам - я бы для начала проверил вызов repaint др контрола (из рисования графика). Да, это не будет работать на ОС с нативным double-buffer, ну так может это для Вас и не актуально. Если работает - то через notify отловить update (для которого не было парного paint) и после выхода из sendPostedEvents самому сделать repaint.

Все-таки оптимизация самого процесса рисования выглядит привлекательнее (то конечно легко говорить  :))


Название: Re: [qwt] Производительность при перерисовки графика
Отправлено: Akon от Январь 13, 2014, 14:11
Ок. Спасибо.
Цитировать
а также не использует нативные контролы, т.е. сам рисует виджеты
Я имел ввиду, что системные дескрипторы на эти виджеты не создаются, следовательно, система о них ничего не знает и при перерисовке торкает их парента, который с дескриптором (QMainWindow?). Буду знать про MacOS.