Опыт обновления версии Qt в FastRawViewer

Бэрримор, кто это ночью выл на болоте?
Простите, сэр, накопилось!

Мы тут, в порядке общего улучшения всего, осуществили переезд с версии Qt 5.4 (на Mac использовалась вовсе 5.3, потому что в 5.4.2 были некоторые глюки с OpenGL) на Qt 5.12.

Начинали с Qt 5.12.5, закончили Qt 5.12.8 + патч, который будет только в Qt 5.12.9. Незадолго до того, переехали вовсе с Qt 4.8.5 на ту же 5.12 проектом RawDigger.

Причин для такого переезда накопилось много, вот те, что смог вспомнить:

Совместимость (точнее несовместимость) с новыми версиями macOS

До macOS 10.14 Mojave мы держались. Ну то есть «было бы неплохо обновить версию Qt», но запланировано это было на FRV 2.0. С появлением macOS Catalina внезапно появилось заметное количество жалоб, в частности

  • Qt 4.8.5 на 10-битных дисплеях мог рисовать очень медленно, внутри macOS возникало преобразование цвета, которое на некоторых конкретных маках сильно тормозило (Это удивительно на самом деле, на моем хакинтоше с i7-9700 и тоже 10-битным дисплеем – тормозящее место видно в профайлере, но не напрягает. А жалобы поступали от владельцев iMac Pro с 18-ядерным CPU).
    Эта жалоба, впрочем, относится к RawDigger и там переход к Qt 5.12 произошел практически безболезненно, нет таких требований к скорости отрисовки.
  • Qt 5.3 на тех же дисплеях мог аналогично тормозить при прокрутке большого количества thumbnails, но про это будет ниже отдельная секция.
  • Проблема с отображением китайских имен файлов и папок: на macOS 10.14 все было отлично, а на 10.15 с теми же папками отображаются квадратики вместо иероглифов («что-то со шрифтами»)
  • Нет поддержки dark theme (она не очень нужна FRV, но тем не менее).

Проблемы с HiDPI на Windows

Qt 5.4 очень странно себя ведет при Windows Font Size: 300%, меню кривые, списки кривые. Qt 5.12 ведет себя тоже не идеально, но хотя бы меню не помятые. FRV при таком размере (увеличении?) шрифта себя тоже ведет неидеально, ждем Qt 5.12.9 (где поддержка HiDPI будет улучшена в очередной раз) и тогда займемся проблемой сверхбольших шрифтов отдельно. Судя по тому что я читаю в рассылках Qt, сама по себе проблема весьма нетривиальная, особенно в мульти-мониторной конфигурации с разным увеличением шрифтов.

Итак, берем новую версию Qt и собираем с ней FRV

В первом приближении – все работает.

Во втором приближении, нужно в маковский версии убрать тот код, который присобачен для Qt 5.3 и все работает еще лучше (в частности, на маке теперь можно включить скроллбары в главном окне программы, раньше они не работали вовсе).

В третьем приближении можно почистить код, который обеспечивал всякое странное на старых версиях Mac OS X (например, если кто не помнит, Fullscreen режим появился в Mac OS 10.7, до того было только Maximized, а в 10.7 он был optional).

В четвертом приближении начинаются problem reports, про которые ниже в хронологическом порядке. Прежде чем перейти к ним, важное уточнение.

Совместимая версия

Внутренний голос (из области пониже спины) с первого дня перехода на новый Qt прямо орал: имейте запасную сборку.
И мы ее имели (и продолжаем иметь для мака): это все тот же самый FRV, но собранный со старой Qt (5.4/5.3). Да, эта версия имеет все те же проблемы, ради которых и был затеян переезд (китайские имена файлов, не поддерживается dark theme и так далее, все перечислено выше). Но возможность сразу, мгновенно, выдать пользователю «совместимую» версию и, с некоторой вероятностью, получить от него ответ «спасибо, помогло» (или, хотя бы, не получить ответа «не помогает») - это бесценно. Если «совместимая» версия помогает, то это с очень большой определенностью указывает на проблемы «в» или «с» новой версией Qt.

Теперь, собственно, про проблемы.

Скорость отрисовки QListView на macOS

Это прямо целая опупея, пишу по памяти, могу что-то пропустить:

  • Qt 5.12.5 – все работало прилично и прямолинейно, окно приложения всегда обновлялось целиком. Скорость не феноменальная (на 5k display не получались 60fps), но приемлемая (40-50fps на 5k)
  • Qt 5.12.6/7 – были сделаны улучшения, которые приводили к избыточному и очень медленному на некоторых маках/с некоторыми дисплейными профилями преобразованию цвета. Скорость упала до 5fps на системах которым не повезло.
    В результате, одна из версий FRV собиралась с Qt 5.12.7 но с перерисовкой экрана от 5.12.5
  • Qt 5.12.8 – сделано частичное обновление экрана (т.е. если у вас обновился один widget, то перерисовывается только он), все казалось бы неплохо, но
    • macOS аллоцирует backing store с питчем, кратным 64 байтам (т.е. 16 пикселям)
    • в коде отрисовки проверяется, что размер строчки backing store равен width * 4, если да, то работает быстрый путь, а если нет – то медленный, с лишним копированием и конверсией данных.

Последний пункт очень смешной: в тестовых примерах обычно пишут …setwindowsize…(800x600) (ну или 1200x1000 или какие-то другие круглые(!) числа и все работает по быстрому пути. Аналогично, в fullscreen-режиме размер окна равен размеру экрана, который в свою очередь кратен 16-ти.

А вот в реальной жизни, когда размер окна установлен как-то вручную мышкой, вы с вероятностью 15/16 не сделаете его кратным 16-ти и исполнение пойдет по медленному пути.

Это все видно на профайлере, результатом стал вот такой вот патч, который войдет в Qt 5.12.9, а FRV мы пока собираем с Qt 5.12.8 + этот патч: https://codereview.qt-project.org/c/qt/qtbase/+/300297/4/src/gui/kernel/qplatformgraphicsbufferhelper.cpp

Форматирование таблиц

В Qt 5.4 работал вот такой вот код:

              tableView->verticalHeader()->setDefaultSectionSize(height);

И все строчки таблички, включая заголовочную, было одинаковой (и указанной) высоты

В Qt 5.12 этого недостаточно, там нужно так:

              tableView->verticalHeader()->setDefaultSectionSize(height);
              tableView->verticalHeader()->setMaximumSectionSize(height);

И это сформатирует все, кроме заголовка. А заголовку еще нужно вернуть нужный размер через (QAbstractItemModel::)headerData для роли SizeHintRole.

Патч в коде – на 8 строчек, чтобы написать эти 8 строчек потребовалось половина рабочего дня (стили не работают, sizeHint работает частично, пробы и ошибки, ошибки и пробы).

Зависание при расчете размеров QTextDocument

Опять макос, опять единичные жалобы «ваша новая версия FRV виснет прямо на старте».

Хорошо, что в макоси есть встроенные средства выдать стек зависшего (или упавшего) приложения, каковой стек четко указывал на расчет размера rich text строчки (но вот какой именно – из стека не понять).

QTextDocument использовался для красивого форматирования EXIF summary: мелкий шрифт для mm у фокусного расстояния, значок вспышки если была вспышка, подчеркивание у GPS coordinates.

Ну пришлось напрячься и сделать все то же самое уникодом (поэтому значок вспышки в последних версиях FRV сильно изменился – что есть в шрифте, то и кажем).

Падение СИСТЕМЫ при работе с NTFS на маке

Этот пункт последний по счету, но, конечно, первый по важности.

У некоторых (единичных) пользователей FRV стал уносить систему. Нажал пару кнопок в FRV – перезагрузился. Жалоб было немного (ну может одна в пару недель), но жалобы очень неприятные.

В жалобах был

  • Один system crash report, который указывал на парагоновский драйвер NTFS (не последней версии, но именно эта версия раздается как бесплатное приложение к некоторым сигейтовским дискам).
  • Совпадающие описания что «все падает, когда я делаю move to _rejected»

Когда делается «move to _rejected», FRV делает два вызова:

QDir::mkdir()
QFile::rename(newname)

Ну и пошел я читать исходники Qt, выяснилось что

Qt 5.3 делает QFile::rename() простым вызовом rename(2): https://github.com/qt/qtbase/blob/5.3/src/corelib/io/qfilesystemengine_unix.cpp#L631

Qt 5.12 делает то же самое СЛОЖНО: https://github.com/qt/qtbase/blob/v5.12.8/src/corelib/io/qfilesystemengine_unix.cpp#L1244

Я даже не стал вдаваться, что именно выполняется в случае macOS, renameat2() или renameatx_np() (ну то есть не стал проверять, срабатывает ли условие компиляции #if defined(RENAME_NOREPLACE) && QT_CONFIG(renameat2) ), а просто запатчил исходники Qt 5.12, вернув туда простой старый rename()

У меня до сих пор нет 100% уверенности что проблема именно тут, но с выкаткой версии «с патченным Qt» в https://www.fastrawviewer.com/download все жалобы на «система падает» прекратились, прошло правда всего две недели и, повторяю, уверен я пока процентов на 99%.

Мерцание экрана при обновлении

В Qt 5.4 в некоторых случаях требовалась принудительная перерисовка экрана.

В Qt 5.12 эта перерисовка приводит к мерцанию (и убрана под #ifdef соответствено)

P.S.

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

С минимальным количеством содержательных(*) #ifdef на довольно большой кодовой базе удается поддерживать Windows начиная с XP и macOS начиная с 10.6 (и OpenGL shaders с OpenGL и DirectX backing).  Других вариантов сделать такое – я просто не вижу.

Как это выглядит без Qt – видно на примере работы со сменными накопителями. Поддержки такой функциональности в Qt нет, поэтому написано руками, отдельно для Windows, отдельно для Mac – и это абсолютно разный код на двух платформах (что неудивительно) и кода этого в сумме почти 6к строчек, где-то по трети каждого типа (mac-специфичный, win-специфичный и общий интерфейс над этим вот всем). И это только один небольшой и несложный кусочек.

(*) есть еще довольно много #ifdef отвечающих за показ, поскольку стандартные margins у macOS великоваты и там их надо много где уменьшать.

P.P.S.

Ежики плакали, кололись, но продолжали есть кактус, да.
 

Comments

> А вот в реальной жизни, когда размер окна установлен как-то вручную мышкой, вы с вероятностью 15/16 не сделаете его кратным 16-ти и исполнение пойдет по медленному пути.

А в этой вашей [censored] макоси совсем нельзя перехватывать событие "масштабирование окна мышкой" и принудительно округлять размер?
В винде-то бездуховной можно было в обработчике WM_SIZING соломки подстелить, ЕМНИМС. Может, и тут что-то такое есть?

Можно. Это можно и в Qt сделать платформенно-независимым образом (все платформенно-зависимое внутри Qt разрулится)

Но лучше ж починить прямо в основании, верно?

Как всегда, половину не понял, но очень интересно.

Простыми словами
- теоретически, внутри одной major-версии Qt (5-й к примеру) обещано что "заменяете DLL на более новые и все работает"
- практически это не вполне так :)

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

Заковыристая формулировка - но да.

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

«Вы там держитесьобновляйтесь, а мы тут посмотрим, тащемта».

Погодите вот Apple на свой ARM переползет - вообще весело станет.

Ну, гм, да. И нет.
Ну в смысле - нужно тогда нести все на Metal и то ли значит Qt6, то ли хрен пойми.

Насколько я понимаю текущую философию Apple - только Metal. Вот и ломай голову кроссплатформенным продуктам с реализацией всей этой фигни.

Непонятно. OpenGL ES все еще ж есть на iOS.
И в следующей *вроде бы* не выключат.