Как в программе на Qt быстро выводить линии? (из большого числа точек)

Модератор: Модераторы разделов

MiK13
Сообщения: 954
ОС: Linux Debian

Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

В АРМе, который мы делаем, есть экран, на который выводится информация. А именно изображение, надписи и различные линии (сетка).
Недавно возникла потребность выводить ещё и некий контур из большого числа точек. И тут возникла проблема.
Сам АРМ делали другие люди, я только некоторые его функции и вспомогательные программы на C.
Я Qt и его функции знаю плохо и поэтому делал по аналогии с тем, что делали другие. И вывод контура сделал примерно так

Код: Выделить всё

    painter->begin(this);
    for(i=1,i<n,i++)
        painter->drawLine(x[i-1],y[-1],x[i],y[i]);
    painter->end();
И оказалось, что если точек примерно 8 тысяч, то картинка выводится с частотой 32-33 кадра в секунду.
Если точек порядка 15 тысяч, то скорость вывода падает до 20 к/с
А если 45 тысяч, то вообще до 7-8 к/с. При этом top показывает загрузку этим процессом 100%.
Может быть есть функции Qt, которые позволяют выводить информацию гораздо быстрее?

В аналогичной программе, но написанной на С с использованием FreeGLUT я эту же информацию вывожу примерно таким кодом

Код: Выделить всё

  glBegin(GL_LINE_STRIP);
  for(i=0;i<n;i++)
    glVertex2d(x[i],y[i]);
  glEnd();
При этом количество точек в линиях (8к, 15к или 45к) очень мало сказывается на скорости вывода. И загрузка процессора при этом увеличивается всего от 37% до 42%.

Есть ли в Qt функции, которые бы позволили увеличить скорость вывода в несколько раз?
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18294
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как в программе на Qt быстро выводить линии?

Сообщение Bizdelnick »

Ничего не знаю про Qt, но вот это читали?
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
devilr
Сообщения: 3000
ОС: Mandriva => Gentoo (~amd64)

Re: Как в программе на Qt быстро выводить линии?

Сообщение devilr »

А не проще сначала сформировать всю картинку, а потом вывести на экран? Как одно из решений.
Мудрость приходит с возрастом.
Иногда возраст приходит один.
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

Bizdelnick писал:
08.05.2020 14:02
Ничего не знаю про Qt, но вот это читали?
Сейчас почитал. Но тут такое дело.
Первоначально я весь вывод написал с использованием функций OpenGL, точнее GLUT. По аналогии с тем, как сделал это в своей программе.
Вывод изображения через glTexImage2D, а линий -- как описал выше.
Но потом основной разработчик АРМа решил, что надо использовать функции Qt. Аргумент: вместо 20-30 строк кода будет 2-3.
Реальность, правда, оказалась немного другой. Так как изображение надо увеличивать. Но замена glTexImage2D(..._ на painter->drawImage(...), похоже, даже снизила нагрузку на процессор. Но когда потребовалось рисовать контуры из большого числа точек...
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3724
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как в программе на Qt быстро выводить линии?

Сообщение Hephaestus »

devilr писал(а):
08.05.2020 15:16
А не проще сначала сформировать всю картинку, а потом вывести на экран?
Мысль правильная, но зависит от картинки.
Если там нужен вывод в реальном времени (типа анимация),
то отрисовка в цикле - первое, что приходит в голову и работает крайне медленно.
А по-хорошему, будет нужна "смена кадров" (помнится, в старых паскалях это реализовывалось сплайнами).
А как такие штуки делают в Qt, я не знаю.

Но если контур статичный, то формирование контура заранее и последующая отрисовка "целиком",
будет значительно быстрее - это без сомнения.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

devilr писал(а):
08.05.2020 15:16
А не проще сначала сформировать всю картинку, а потом вывести на экран? Как одно из решений.
Я так делал в самой первой версии АРМа, которая использовала SVGALib. Заодно скидывал всё в файл.bmp на /dev/shm/ и конвертировал его в .gif. Но потом решили всё делать на Qt.
А самому формировать картинку... так надо ещё писать функции рисования линий, прямоугольников, причём выводить с преобразованием "физических" координат а экранные да и с учётом увеличения фрагмента... В OpenGL это делается просто. А самому...
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

Hephaestus писал:
08.05.2020 16:22
Но если контур статичный, то формирование контура заранее и последующая отрисовка "целиком",
будет значительно быстрее - это без сомнения.
А как его сформировать заранее?
Я думал запустить его отрисовку в отдельном потоке. Но в Qt с его классами пока не разобрался как сделать.
К тому же подозреваю, что выведенный контур будет затираться растровым изображением, которое надо выводить непрерывно.
Спасибо сказали:

Аватара пользователя
devilr
Сообщения: 3000
ОС: Mandriva => Gentoo (~amd64)

Re: Как в программе на Qt быстро выводить линии?

Сообщение devilr »

MiK13 писал:
08.05.2020 16:24
В OpenGL это делается просто. А самому...
Так и самому несложно. Растр - это ведь всего лишь двумерный массив.
Впрочем, с изучением 100500-го фреймворка, иногда, базовая математика и геометрия забываются напрочь, обычно. :)
Мудрость приходит с возрастом.
Иногда возраст приходит один.
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18294
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как в программе на Qt быстро выводить линии?

Сообщение Bizdelnick »

MiK13 писал:
08.05.2020 16:19
Первоначально я весь вывод написал с использованием функций OpenGL, точнее GLUT. По аналогии с тем, как сделал это в своей программе.
Вывод изображения через glTexImage2D, а линий -- как описал выше.
Но потом основной разработчик АРМа решил, что надо использовать функции Qt. Аргумент: вместо 20-30 строк кода будет 2-3.
Реальность, правда, оказалась немного другой. Так как изображение надо увеличивать. Но замена glTexImage2D(..._ на painter->drawImage(...), похоже, даже снизила нагрузку на процессор. Но когда потребовалось рисовать контуры из большого числа точек...
Не уловил мысль. Qt умеет работать через OpenGL, тут нет никакого противоречия. Просто надо это задействовать.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3724
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как в программе на Qt быстро выводить линии?

Сообщение Hephaestus »

MiK13 писал:
08.05.2020 16:27
А как его сформировать заранее?
Как сделать именно в Qt, я не знаю.
Я с задачей отрисовки имел дело два раза в жизни (во время учёбы).

Если интересно, под спойлером чуть подробнее.
Spoiler
Первый раз была задача вывести на экран флаг, развивающийся на ветру.
Это ещё был старый паскаль (под DOS). Вот как раз здесь использовались сплайны.
Основная идея: "рисуем" кадр "в памяти" (в специальной области),
затем сразу весь кадр выводим на экран. Пока оно выводится, в памяти уже рисуется следующий кадр,
потом он выводится и т.д. Чтобы не было мелькания к этим кадрам применяются разные режимы вывода (что-то вроде ТМО). У меня решить задачу этим способом не получилось, точнее результат мне не понравился (всё-таки не удалось избежать мелькания).
И я отрисовывал как раз "на лету" в цикле. Работало настолько медленно, что плавное движение получилось само собой просто в силу медленной отрисовки.

Вторая задача была на третьем курсе. Реализовывали векторный графический редактор.
Это было уже под Win в Delphi.
Там нужно было сделать кучу всякой всячины (линии, стрелки, звёздочки с произвольным числом лучей) и ТМО между ними.
Естественно, всё это выделялось, перетаскивалось по экрану, вращалось, растягивалось и деформировалось.

Плюс к этому в процессе создания, когда объект уже виден, но ещё не отпущена клавиша мыши, объект отображался подвижным временным контуром. Ну, скажем, выбрали инструмент "звездочка", зажимаем левую клавишу, двигаем по экрану. Звёздочка "рисуется от центра", виден предварительный контур. Пока клавиша мыши не отпущена, этот контур можно "растягивать" или, наоборот, "сжимать" (изменяя тем самым размеры будущего объекта) и поворачивать вокруг центра. Ну, Вы это видели в любом векторном редакторе.
Вот примерно это мы и делали.

Вот здесь, несмотря на всё перечисленное богатство, проблем со скоростью отрисовки не было вообще, как ни странно. То ли машины уже были быстрее, то ли, сама среда разработки позаботилась... А упомянул я об этом потому, что да, мы пробовали, разные способы рисования контуров, алгоритмы заливки, ТМО и пр. Целая отдельная дисциплина была "Компьютерная графика".
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

Bizdelnick писал:
08.05.2020 16:51
Не уловил мысль. Qt умеет работать через OpenGL, тут нет никакого противоречия. Просто надо это задействовать.
Первоначально было именно через OpenGL (FreeGLUT). Но потом решили переделать это через функции Qt.
И от моего предложения вернуться к OpenGL основной разработчик АРМа отказался.
Но для меня как-то странно, что есть такая большая разница между выводом через painter->drawLine и через glVertex2d.
Или, может быть, painter->drawLine непосредственно рисует линию, а glVertex2d просто отправляет координаты видеоадаптеру, который и сам и рисует после glEnd() ?
Может быть и в Qt есть что-то подобное?
Там я увидел функции painter->drawLines, которым можно задать линию и количество точек в ней. Или как-то ещё. Но у них параметры -- не простые типы или структуры, а классы Qt. С которыми я не знаком. И которыми никто у нас ещё не пользовался.
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

devilr писал(а):
08.05.2020 16:42
MiK13 писал:
08.05.2020 16:24
В OpenGL это делается просто. А самому...
Так и самому несложно. Растр - это ведь всего лишь двумерный массив.
Впрочем, с изучением 100500-го фреймворка, иногда, базовая математика и геометрия забываются напрочь, обычно. :)
Вот по этой причине я и предпочитаю писать на C, на на C++. А тем более Qt, где надо запоминать кучу функций, да ещё когда каждая имеет по несколько вариантов в зависимости от типа и числа параметров.
А самому не сложно когда картинка одного размера. А когда её нужно увеличить чтобы детальнее показать какой-то фрагмент...
В OpenGL я просто задаю координаты виртуального поля. Да и в Qt это оказалось не сложно.
И, кстати, возникла ещё одна проблема.
В OpenGL вертикальная координата идёт снизу вверх, а в Qt сверху вниз. Пришлось в программу формирования растровой картинки добавлять переворот. Из-за этого в моей программе возникла проблема (пока не решённая): картинку увеличил, начинаю её сдвигать -- по вертикали растр едет в одну сторону, а линии в другую. Но это мелочи.
Скорее всего вернусь к GLUT'овскому варианту выводу графики.
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 18294
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как в программе на Qt быстро выводить линии?

Сообщение Bizdelnick »

MiK13 писал:
08.05.2020 17:13
Первоначально было именно через OpenGL (FreeGLUT). Но потом решили переделать это через функции Qt.
И от моего предложения вернуться к OpenGL основной разработчик АРМа отказался.
Ещё раз: здесь нет никакого противоречия. Используйте классы, описанные по данной мной ссылке. Вон пример есть.
MiK13 писал:
08.05.2020 17:13
Но для меня как-то странно, что есть такая большая разница между выводом через painter->drawLine и через glVertex2d.
Вы работали с OpenGL, и для Вас это странно? Я вот не работал, но для меня не странно.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
ormorph
Сообщения: 1664
ОС: Gentoo

Re: Как в программе на Qt быстро выводить линии?

Сообщение ormorph »

Глянул в assistant, там есть такие прототипы:
Spoiler

Код: Выделить всё

[virtual] void QPaintEngine::drawLines(const QLineF *lines, int lineCount)
 
The default implementation splits the list of lines in lines into lineCount separate calls to drawPath() or drawPolygon() depending on the feature set of the paint engine.

Код: Выделить всё

[virtual] void QPaintEngine::drawLines(const QLine *lines, int lineCount)
This is an overloaded function.
The default implementation converts the first lineCount lines in lines to a QLineF and calls the floating point version of this function.

Код: Выделить всё

void QPainter::drawLines(const QLine *lines, int lineCount)
This is an overloaded function.
Draws the first lineCount lines in the array lines using the current pen.

Код: Выделить всё

void QPainter::drawLines(const QLineF *lines, int lineCount)
Draws the first lineCount lines in the array lines using the current pen.
See also drawLine() and drawPolyline().
Т.е. можно передавать сразу массив линий(список) типа QLine либо QLineF. возможно это будет работать быстрее.
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3724
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как в программе на Qt быстро выводить линии?

Сообщение Hephaestus »

MiK13
Вообще, всё зависит от того, что там отрисовывается.

Если эти 20 тыс. точек Вы получили, скажем, со спутника, и важны именно сами точки,
то есть каждую надо воткнуть на её место и ничего не потерять, тогда да, надо обрабатывать массив точек.
Но даже здесь можно сэкономить, убрав повторы. (Рисуя на экране точку, Вы закрашиваете пиксель. Если другая точка закрашивает этот же пиксель, первую уже не видно. Поэтому отрисовывать требуется только последнюю точку с данными координатами, а не все.)

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

Если же у Вас растровая картинка (фотография), то её тем более не стоит выводить попиксельно.
Её нужно выводить сразу.

Да, массив точек, казалось бы, хорош тем, что можно получить любую картинку, просто воткнув каждую точку на её место. Но как видите, это бывает медленно.

Поэтому, как я уже сказал, всё зависит от того, что там рисуется.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
ormorph
Сообщения: 1664
ОС: Gentoo

Re: Как в программе на Qt быстро выводить линии?

Сообщение ormorph »

Можно чтобы линия состояла из точек:

Код: Выделить всё

painter->setPen(QPen(Qt::black, 1, Qt::DotLine, Qt::RoundCap)
Все равно для окантовки нужно.
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

Есть растровая картинка которая непрерывно обновляется. На неё накладывается координатная сетка и сейчас ещё добавились контуры объектов. И всё это надо увеличивать в любом месте. Причём значения точек в другой системе координат, не экранной. Поэтому их надо преобразовывать. Но это не занимает много времени. И вся линия не является непрерывной. Поэтому нельзя весь массив координат вывести одной функцией.
Я, пожалуй на следующей неделе вернусь к тому выводу, который был в предыдущей версии АРМа, через функции FreeGLUT. Которые и линии из сотен тысяч точек выводят с приемлемой скоростью.
Спасибо сказали:

Аватара пользователя
ormorph
Сообщения: 1664
ОС: Gentoo

Re: Как в программе на Qt быстро выводить линии?

Сообщение ormorph »

Хм, вроде как в QT надо еще учитывать, что виджеты находящиеся между строчками:

Код: Выделить всё

QApplication app( argc, argv );
......
......
app.exec();
и подобным им, обрабатываются в цикле. По этому желательно избавиться от дурной работы по отрисовке каждой точки в цикле.
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 3724
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как в программе на Qt быстро выводить линии?

Сообщение Hephaestus »

MiK13 писал:
08.05.2020 23:50
Есть растровая картинка которая непрерывно обновляется. На неё накладывается координатная сетка и сейчас ещё добавились контуры объектов. И всё это надо увеличивать в любом месте.
Насчет "увеличивать" ничего не скажу.
А вот координатная сетка - это набор прямых линий, причем параллельных/перпендикулярных.
Их вот точно не стоит выводить попиксельно.
Что касается контуров, если это контуры произвольной формы (всякие там выпукло-вогнуто-ломано-квадратно-полукруглые), то их придется по точкам, да. Но опять-таки, лучше отрисовать контур "в памяти", а потом сразу вывести на экран. А если это контуры однотипные (квадраты-треугольники), их тоже можно строить на примитивах, опять-таки заранее.
MiK13 писал:
08.05.2020 23:50
Я, пожалуй на следующей неделе вернусь к тому выводу, который был в предыдущей версии АРМа, через функции FreeGLUT. Которые и линии из сотен тысяч точек выводят с приемлемой скоростью.
Так-то хозяин - барин. Не мне Вас учить.
Но я бы обратил внимание на то, что задача слегка изменилась, насколько я понял.

Добавились контуры объектов, которых раньше не было.
С учетом этого, старый способ может оказаться уже не таким быстрым, как раньше.
С увеличением количества объектов скорость снизится. И зависимость там нелинейная.
То есть навскидку так и не скажешь, в какой момент оно вдруг просядет.
Ну, к примеру, набор из ста объектов отрисовывается нормально, а если там сто один объект - оно уже застряло. Поэтому я бы, всё-таки посмотрел в сторону оптимизации. Ну, как минимум, потестировал бы разные варианты.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
ormorph
Сообщения: 1664
ОС: Gentoo

Re: Как в программе на Qt быстро выводить линии?

Сообщение ormorph »

Есть более простые функции рисования.
Класс QPainterPath, функции moveTo для начальной точки, и продолжать уже от данной точки линию lineTo. Ну а стиль линии уже задавать через painter->setPen.
Спасибо сказали:

Аватара пользователя
devilr
Сообщения: 3000
ОС: Mandriva => Gentoo (~amd64)

Re: Как в программе на Qt быстро выводить линии?

Сообщение devilr »

Так рисование через painter не предполагает использование аппаратного ускорения. А в самом начале как раз предлагалось использование OpenGL, который как раз использует аппаратное ускорение. Поэтому, я и предлагал рисовать в картинку, а уж потом и выводить её на физическое устройство.
Мудрость приходит с возрастом.
Иногда возраст приходит один.
Спасибо сказали:

Аватара пользователя
ormorph
Сообщения: 1664
ОС: Gentoo

Re: Как в программе на Qt быстро выводить линии?

Сообщение ormorph »

devilr писал(а):
09.05.2020 21:04
Так рисование через painter не предполагает использование аппаратного ускорения. А в самом начале как раз предлагалось использование OpenGL, который как раз использует аппаратное ускорение.
Скорее всего загрузка процессора происходит из за того, что идёт постоянная перерисовка контура, так как растровая картинка постоянно обновляется, то и контур я так понимаю постоянно перерисовывается.
Спасибо сказали:

Аватара пользователя
devilr
Сообщения: 3000
ОС: Mandriva => Gentoo (~amd64)

Re: Как в программе на Qt быстро выводить линии?

Сообщение devilr »

Ну, тогда можно использовать родной QOpenGLWidget и рисовать в него. Если уж так хочется чистый Qt.
P.S. Хотя, мне кажется, что стоит пересмотреть архитектуру.
Мудрость приходит с возрастом.
Иногда возраст приходит один.
Спасибо сказали:

MiK13
Сообщения: 954
ОС: Linux Debian

Re: Как в программе на Qt быстро выводить линии?

Сообщение MiK13 »

ormorph писал(а):
09.05.2020 23:16
Скорее всего загрузка процессора происходит из за того, что идёт постоянная перерисовка контура, так как растровая картинка постоянно обновляется, то и контур я так понимаю постоянно перерисовывается.
Если бы можно было как-то выводить растровую картинку, не трогая контур, то это было бы очень хорошо. Но пока я не знаю как это сделать. Поэтому и приходится рисовать растр (который надо постоянно обновлять), а потом всякие рисунки и надписи
devilr писал(а):
09.05.2020 21:04
Так рисование через painter не предполагает использование аппаратного ускорения. А в самом начале как раз предлагалось использование OpenGL, который как раз использует аппаратное ускорение.
"Чистый" OpenGL оказался довольно сложным для быстрого решения задачи. Поэтому я использовал библиотеку GLUT. И сделал первоначально "заготовку" на си. А потом уже внёс этот код в основной АРМ на Qt.
Но после перехода на 64-битную версию и новую версию Qt (кое в чём не совместимую с предыдущей) решили, что надо и вывод на экран использовать средствами Qt. В частности классом painter. "Потому, что это гораздо проще". И пока не попробовали выводить контур из десятков тысяч точек, никаких проблем не было.
Пока я решил "разредить" контур, рисуя его по точкам, которые отстоят друг от друга не слишком близко. Посмотрю, что получится.
Спасибо сказали: