OpenGL

Записки сумасшедшего: Qt+OpenGL

Мы продолжаем наш репортаж про Qt и OpenGL.

В Qt, если кто не знает, есть приятные врапперы для OpenGL. QOpenGLShaderProgram для шейдеров, QOpenGLBuffer для буферов ну и так далее. Я ими пользуюсь, потому что удобно, сахарок вкусный типа QOpenGLVertexArrayObject::Binder, опять же деструкторы все удалят и так далее.

И есть QOpenGLTexture, любовь с ней в FRV у меня не задалась (делаю все руками в результате), а тут в рамках HelloWorld-2017 решил вернуться к ней.

И получился (почти)...

Если к кошке приставить голову тигра....

Каких-то жалких 4.5 года назад KDAB начали рекламировать использование современного OpenGL изнутри Qt.

Я их начитался еще тогда и возжелал. Однако требования к железу (OpenGL 3.3/DirectX 11) поднимать в FastRawViewer не хотелось и руки дошли вот только сейчас, для нового проекта, благо железо подросло и OpenGL 3.3 есть во всем вменяемом (включая Intel, кто до сих пор работает на SandyBridge с тамошним видео - пусть страдает).

Однако научные исследования показали, что реально OpenGL CoreProfile так чтобы все...

Лучшее - враг....

After Qt 5.3’s introduction of QQuickWidget, Qt 5.4 adds QOpenGLWidget, the long-awaited replacement for the legacy QGLWidget.

Писали нам в Qt Weekly три года назад.

Я тогда попробовал, но Qt 5.4 глючила на маке (уже не помню даже в каком месте), держать две ветки кода было не с руки, так и используем legacy QGLWidget в FRV.

Прошло три года и руки дошли попробовать еще раз, уже в Qt 5.6. Такой вот бутерброд (если вкратце):

QGraphicsView...

Q: Qt, Dynamic OpenGL, OES/ARB extensions

Вот что-то наткнулся и смотрю как на новые ворота.

Преамбула:

  1. Вот есть такой Qt, в нем есть работа с OpenGL
  2. У кого OpenGL нету или кривой - есть библиотека ANGLE, которая эмулирует OpenGL ES2 (или 3) поверх DirectX 9 (или, соответственно, 11).
  3. OpenGL ES от простого отличается, в числе прочего, названиями extensions. Ну, к примеру, GL_OES_get_program_binary у ES/GL_ARB_get_program_binary у обычного.
  4. У Qt 5.4 (под Windows) появилась возможность динамической загрузки имплементации OpenGL. До инициализации QApplication говоришь что хочешь, а дальше
  5. ...

Немого кино уже нет, звукового кино еще нет....

Вот есть некая программа, которая активно использует OpenGL.

Используется Qt5 и все было бы хорошо, если бы не моментики:

  • QWidget::showFullScreen() не работает на Mac OS X 10.6
  • Курсор в форме руки - остается таковым и при выносе мыши за окно программы (тоже на Mac, на винде все нормально).
По первому случаю - я засабмитил баг, прямо вот 1-го января. Но он все еще в состоянии Not Evaluated (что неудивительно, если посмотреть на график количества багов в Qt за последние 30 дней). По второму - вроде нашел багрепорт, им как-то занимаются.

Ладно, у меня ничего специфического от Qt5 нету, собираем все то же самое на Qt 4.8.4. Работает (ну, пришлось поменять QOpenGLProgram на QGLProgram и так далее в том же духе, но изменений - мало).

Но:

На операционках, запущенных под VMWare (и Windows и Mac) - валится в QGLFunctions::initializeGLFunctions(), судя по отладчику - GL-контекст в этом месте нехорош, дальше не разбирался).

При этом, Qt5 на этих же виртуальных машинах - работает, возможностей тамошнего OpenGL вполне хватает.

На настоящих железных компьютерах с настоящим OpenGL - версия собранная с 4.8 - работает, а неприятные мне баги на маках - отсутствуют. Фулскрин работает, курсор меняет форму как надо.

Ну и как с ними жить? Баги Qt5 явно быстро не починят. Багу Qt 4.8 - скорее всего тоже не починят. Не, ну я могу gl....() сам порезолвить, но обидно же ж.

Q: "OpenGL на CPU"?

Преамбула

Несмотря на долбанутость OpenGL-ного API (и, как следствие, огромное количество глупых ньюбских ошибок, которые я допустил), сам OpenGL мне очень понравился.

Вот, к примеру, код в RawDigger, который занимается визуализацией, это 500+ строк довольно нетривиального кода которые делают:

  • Raw Composite
  • Поканальный показ
  • Overexposure/Underexposure
На OpenGL шейдер, который делает ровно то же самое (ну, да, немножко поменьше, в RD есть всякие режимы 2x2 pixels) - это 11 строк кода, несколько строк определения переменных, ну и, да, еще строк 10 для формирования матриц-векторов на которые мы множим и с которыми сравниваем. Ну и работает "немножко" побыстрее.

К сожалению, выяснилось что полноценный OpenGL есть не везде и в новом проекте таки придется делать софтверный рендеринг.

Амбула

Вопрос, собственно, такой: а есть какой-то code generator, который питался бы чем-то вроде кода OpenGL шейдеров и порождал бы код на SSE+треды. И был бы заточен именно под Imaging.

Ну если быть точным, то я знаю уже про

  • ISPC, который я нежно люблю, но он все-таки не про Imaging (4 рядом лежащих компонента пикселя), а больше про скалярное. С пикселями там приходится чудовищно извращаться.
  • Halide - ровно про это, вот прямо таки заточен, но мне очень не хочется таскать с собой llvm runtime, а хотелось бы генерировать статический код при сборке проекта. Кроме того: Windows support is technically feasible, but we have not yet built or tested on Windows, а мне оно нужно исключительно под Винду, у маков и так все хорошо (даже софтверный рендеринг в виртуальной машине - работает и быстро).
  • llvmpipe - даже совсем OpenGL, но есть серия но:
    • Очень не хочется таскать с собой здоровенный llvm runtime
    • Судя по обсуждению в багзилле мозиллы, прикручивание llvmpipe в качестве software renderer есть процесс весьма сексуальный.
Вопрос мой: может быть есть, все-таки, какие-то оффлайновые кодогенераторы, которые сочетали бы оффлайновость ISPC и пикселе-заточенность Halide/Mesa3D?

Хозяйке на заметку: Qt, OpenGL и PBO

В режиме записок: на этой проблеме впустую потерял дня два минимум, если не больше. Вот, чтобы никогда более, запишу.

Вот есть QGraphicsView, направленный в QGLWidget. Есть еще QGraphicsScene, у которой с помощью drawBackground() рисуем нужное нам (картинку).

Этот самый drawBackground() использует текстуры, одну или много, текстуры залиты через PBO.

Дальше - пытаюсь вывести на эту Scene другие объекты. Ну, как в документации написано, к примеру так:

QLabel *label = new QLabel("Bla-Bla");
scene->addWidget(label);
(ну чуть побольше кода, потому что прозрачность, возможность двигать, но смысл именно такой)

Результат:

  • Если объекты выводятся, окно открывается и т.п. до создания "моих" текстур (которые рисуются на фоне) - то все отлично.
  • Если все разлеглось так, что свои текстуры и PBO я создал до первого показа (даже не создания) объекта на Scene, то жопа, при добавлении элемента к сцене получаем такое:
    texture upload failed, error code 0x502
    
И так пробовал, и сяк и об косяк, потерял не меньше двух дней (ну то есть - не работает, пытаемся создать свой пример, там работает, из примера переносим код к себе - не работает и так с десяток раз). И, внезапно, ответ нашелся:

Если вы работали с QOpenGLBuffer, то прежде чем отдать управление в Qt - сделайте ему release(). Иначе будет нехорошо.

И ведь натыкался я уже на подобное, но с VBO: если не отцепить их, то вообще ничего не работает.

Qt+OpenGL benchmarking Q

А вот, я извиняюсь, такой вопрос.

Есть QGrahicsScene (унаследованное от нее) в которой я в drawBackround вывожу OpenGL-ем нечто.

В процессе разбирательств с OpenGL я это нечто имплементировал десятком разных способов и хочу теперь померять, какой из них быстрее.

Но как?

Ну то есть я сделал бенчмарковый вызов, который зумит (еще панорамирует, переключает нужные мне битмепы и т.п., не суть) в таком вот духе:

for(.....){
myview->setZoom(...);
QApplication::processEvents();
}

Но получается вот что:

  • FPS-ов ровно 60 (2000 итераций за 34 секунды), OGLFormat::setSwapInterval(0) не помог.
  • Одно ядро процессора полностью загружено.
  • По профайлеру, загружено оно QWindowsWindow::raise() (~70% по профайлеру), вообще весь прочий код занимает остальные 30% CPU, моего кода вовсе не видно.

Так слона не продать.

Ну то есть понятно, можно в OpenGL-рисовалке рисовать одно и то же 10 (100,1000) раз подряд, но вопрос заключается в том, нет ли более прямого пути, который позволяет Qt-GUI программы побенчмаркать попроще?

Update: если вдруг кому интересно, то проблема решилась глобальным выключением VSync в настройках драйвера (NVidia). FPS-ы сразу сильно выросли, а профайл исполнения стал похож на настоящий.

Про Qt и OpenGL

С помощью профайлера и отладчика узнал прекрасное.

Прекрасное, конечно же, описано в мануале, но кто же их читает:

QGLContext::DefaultBindOption LinearFilteringBindOption | InvertedYBindOption | MipmapBindOption In Qt 4.5 and earlier, bindTexture() would mirror the image and automatically generate mipmaps. This option helps preserve this default behavior.
А я все удивлялся, отчего у меня bindTexture для ~мегапиксельной картинки выполняется миллисекунд 20. Ну конечно, оно переворачивает изображение.

Оторвал. Стало быстрее раз в 10 минимум (на глазок, по профайлеру).

Из другого прекрасного:

  • Если использовать QOpenGLBuffer::bind() и потом, после рисования или заполнения, не сказать ему QOpenGLBuffer::release(), то в QGraphicsView/Scene отваливается отображение элементов, нарисованных стандартным путем (через addItem).
  • Если генерировать структуры руками (glBindTexture/glTexImage2D), но забыть про установку фильтрации, то текстура вовсе не отображается.
Как я уже писал, сколько же есть вещей, которые я предпочел бы не знать.

День прошел не зря.

OpenGL Qs

Сразу предупреждаю: с OpenGL я пытаюсь работать всего неделю (был еще подход к снаряду, но началось лето и я переключился на МонголиеКарелии), поэтому вопросы у меня, вероятно, глупые и вообще про разное.

Вопросов, собственно, три:

1. Допустим, у меня очень простая сцена, два треугольника, образуют прямоугольник, на них натягиваю текстуру (собственно, картинку, которую хочу показать).

Вопрос: есть ли смысл связываться с VBO или 6 пар вызовов glTexCoord2f()/glVertex2f() не будут заметно медленнее? А если прямоугольник не один, а, к примеру, 64 (картинка 8000x8000, а на предельный размер текстуры я заложусь как 1024x1024), будет ли заметный выигрыш, если загнать все в буфер(ы) и дергать рисование меняя только индексы?

Это мне больше для понимания, понятно что как только захочется что-то нарисовать шейдером, так сразу VBO понадобятся.

2. В Qt5 есть (старые) QGL*-объекты, есть новые QOpenGL*.

Кто бы рассказал, в чем разница....

3. Ну и вообще, Qt-шные примеры, что из комплекта, что найденные в сети, какие-то частично безумные. То слишком простые, то вроде простой - а шейдеры там внезапно "#version 330", то - очень хороший пример boxes из поставки - но переусложненный, начинаешь от него куски отпиливать и все разваливается.

Нет ли каких-то Qt-OpenGL tutorials, которые бы с одной стороны были бы "современными" (в смысле используемых Qt-интерфейсов), интересными (не банальный QGLWidget::paintGL()), но и не слишком сложными.

A: OpenGL book Q: OpenGL compatibility

Отвечаю сам себе, вдруг кому-то еще интересно.

  1. Из всех просмотренных книжек больше всего показалась Learning Modern 3D Graphics Programming, потому что она от fixed pipeline полностью отвязана. Только OpenGL 3+, только хардкор (хотя CoreProfile в Qt и не работает нормально, текстуры не аплоадятся).

    PDF-ная версия и примеры берутся отсюда.

    Буду теперь ея читать.

  2. Вообще, оно (OpenGL) отвратительное. Вот, например:
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    И теперь надо запомнить, что glVertexAttribPointer() работает с тем буфером, которому сделали Bind() последнему. Как-то это не так, неправильно.

    Отчего в glVertexAttribPointer не передается идентфикатор буфера?

  3. Отвратительно оно и в частности. Более всего меня потрясло, когда Qt-шный пример с крутящимися кубиками (и текстурами с цифрами 1-6 на гранях) я запустил на WinXP под VMWare. Кубики крутились, но грань у кубиков была одна, последняя (с шестеркой), предыдущие пять текстур куда-то подевались. Это не OpenGL3 (на VMWare только 2.1). На Win7 в той же VMWare - у кубиков по 6 граней, но отображается на всех - только одна текстура, первая. Смеялсо.
Отсюда вопросы:
  • Оно вообще с совместимостью как? Можно ли рассчитывать, что OpenGL-приложение вообще работает (на случайно отобранной системе), или это сильно как повезет?
  • Сужая предыдущий вопрос: если требовать OpenGL 3+, то не будет ли с совместимостью лучше (на тех системах, естественно, где этот самый 3+ есть)?
  • Ну и чего я лишаюсь, требуя 3+ (т.е. оборудование класса DX10, как я понимаю)? Ну вот я сам накопал: Mac OS 10.6 и старше, интеловская встроенная графика старше SandyBridge, а еще что?

Q: OpenGL book

Вот смотрю я на профили исполнения RawDigger и вижу, что на отрисовку пиксельного буфера там уходит заметное время. Особенно заметное, если картинка здоровая, а кроме перерисовки - ничего не происходит (скажем, каналы RAW переключаются).

Решение, казалось бы, очевидное: загнать это самое RAW в виде текстуры в видеокарту и рисовать дальше уже средствами видеокарты же. Пиксельные шейдеры и все такое. Короче, OpenGL.

Разглядывание (несколько дней на даче сидел и разглядывал) всяких простых OpenGL-ных примеров навело меня на очевидную мысль - я нихрена в этом не понимаю совсем. А пора бы уже.

Отсюда вопрос: какую книжку "OpenGL для чайников за 21 день" вы бы мне посоветовали? На басурманском бегло читаю, наверное надежнее читать исходник, а не перевод.

От OpenGL мне, по большому счету, нужно банальное 2D: показ двумерной картинки, с зумированием (и показ участка), может быть наложение в 2D нескольких слоев с регулируемой прозрачностью. Идеальным был бы tutorial именно в этом вот духе.

Subscribe to OpenGL