Программирование

Про AVX2

Давненько я в этом блоге на ассемблере не писал.

Вот значит кусочек FRV, который накладывает выходную тоновую кривую. Точнее, два кусочка, один на старом добром SSE2 (которого там ровно одна команда, сконвертировать float-int), а второй - на новом модном AVX2, правда 128-битном, но зато с table lookup.

#ifndef USE_AVX
        uint32_t __declspec(align(16)) dpix[4];
        _mm_store_si128((__m128i *)dpix, _mm_cvttps_epi32(pixel));
        dest[col] = 0xff000000 | curve[dpix[2]] <<
...

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. ...

Трудовые будни

Жалуется юзер, странной жалобой: 64-битный FRV падает, 32-битный - работает. Система, естественно, 64 бита.

Через три дампа (научили его снимать через Task Manager) и три версии софта проблема выяснилась:

  • FRV для отрисовки дерева фолдеров использует Qt-шный компонент (QDirModel)
  • Тот, в свою очередь, за иконками ходит в систему, все кончается в SHGetFileInfo()
  • В системе, как мы знаем можно зарегистрировать своего провайдера иконок и рисовать красивые иконки (так делает, к примеру Dropbox: для синхронизированных каталогов-файлов одна иконка, для тех что в процессе - вторая).
  • Ну и у юзера оно так и есть, там DBROverlayIconBackuped.dll, который часть Делловского (ноутбучного?) бэкапа.
  • Ну и 32-битная версия этого добра - дает угля иконок, а 64-битное к ней обращение (уж не знаю битность самого DLL, может где-то по дороге транслируется) - все рушит. Собственно и дампы кончаются в этом DLL, но я только со второго дампа начал подозревать чужую ошибку, а не свою.

Это, граждане, ужас. Везде БЕЗДНЫ.

Из говна и палок

На поведение Windows при вставлении-вынимании флешки я уже жаловался: событие не прилетает, нужно поллить. Потом жаловался и на OS X - событие прилетает, но до того, как устройство появляется в списке томов. Тоже поллинг.

Но действительность на OS X превосходит ожидания.

Если мы размонтируем устройство сами, через FSUnmountVolumeSync(), то поллинг работает нормально: прилетает callback, дальше по таймеру несколько раз читаем список томов, из списка размонтированный том пропадает, все отлично.

А вот если размонтировать через Finder, то жизнь ГОРАЗДО...

WM_DEVICECHANGE на OS X

Продолжение к WM_DEVICECHANGE и все все все (потому и заголовок такой), только на маке.

На OS X, казалось бы, все хорошо, там есть такое:

DARegisterDiskAppearedCallback(m_session, kDADiskDescriptionMatchVolumeMountable, mountCallback2, this);

Ну и дальше в том же духе. И на всовывание USB-карточки прилетает callback.

Дальше мы хотим узнать, что же нам всунули, идем получать список маунтов:

CFURLEnumeratorRef enumerator = CFURLEnumeratorCreateForMountedVolumes(NULL, kCFURLEnumeratorSkipInvisibles, NULL);

Фигак, а нашего тома в этом списке еще нет.

Причем,...

Про QFileSystemModel

А вот возьмем, к примеру, Qt и решим вывести дерево фолдеров (папок, каталогов).

С незапамятных времен в Qt есть QDirModel, которая тянет данные синхронно и, поговаривают, очень от этого тормозит. Не заметил, чтобы очень, но на долю секунды при открытии большого каталога по сети - ну да, есть. Самое плохое в этом то, что оно блокирует UI пока читает каталоги, а пересадить в отдельный (от View) thread, судя по всему, не получится.

А есть новая прекрасная QFileSystemModel,...

WM_DEVICECHANGE и все все все

Обложился USB-кардридерами, сую в них карточки и высовываю и чувствую, что схожу с ума.

Вот есть сообщение WM_DEVICECHANGE, оно прилетает

  1. когда я вставляю USB-флешку (не карту)
  2. когда я подключаю ридер (без карты)
  3. когда я подключаю ридер с заранее вставленной карточкой.

А вот если подключить ридер без карты (прилетит сообщение), а потом вставить карточку - на вставление карточки WM_DEVICECHANGE не прилетает.

Kaspersky Antivirus со мной солидарен, свое "обнаружен съемный диск" он показывает в случаях 1 и 3 (ну а...

Про компьютеры

Есть вот такое мнение, что "компьютеры перестали становиться быстрее". Пару лет назад я его даже развеивал на хайлоаде, но в докладе упирал на SSE/AVX/итп.

Но тут понадобилось попрофайлить кой-чего на Core2 (у котогого aligned/unaligned access резко отличаются). Достал с полки старый свой макбук, 2007-го года, Core2 Duo, ~2.2гигагерца (могу ошибаться, по памяти пишу). Надо теперь собрать Qt. Вот Qt 5.3.2 собиралося у меня почти 7 часов (400 с чем-то минут по time), на двух ядрах.

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

Отобрал у жены ноут. Celeron T3100, то же поколение Сore2, 1.9гигагерца, винды. Вот 2010-я студия собирает, 5 часов уже прошло, до QtDeclarative добрались. Значит еще не меньше часа.

Для сравнения, i7-4770 собирает тот же Qt 5.3, судя по датам создания файлов, 36 минут. В 10 раз быстрее. Да, вдвое больше ядер, ~вдвое больше частота, но еще 2.5 раза просто от большей лучшести.

Это вот я к тому, что те кто гордо не апгрейдит свои Q6600, они на самом деле заметно отстали от.

Загадка DPI

Вот значит вроде порешал проблему:
  • Все шрифты теперь в pt, а не в px
  • Практически все иконки - в SVG (осталась, буквально, одна, но отчего-то в Qt stylesheets не работает svg, хотя по доке - должна, ну соберусь с силами и буду генерировать ее на скаку)
  • Размеры окон, там где надо (автомат работает так, что мне не нравится) - в em
  • чего-то еще вылезло, но вот не могу уже вспомнить
И все на винде заработало прилично. Несу на мак. Собираю. Ой.

И вот гложет меня теперь вопрос:

Отчего на одинаковом мониторе (виртуальном, 1920x1200, никакого HiDPI, никаких специальных настроек, операционки поставлены по дефолту) 9pt шрифт на винде (приблизительно) соответствует по размеру 14pt на маке?. Один и тот же шрифт. Тахома.

Про HiDPI на винде

Поступили жалобы, дескать FRV на Win8(.1?) на HiDPI-мониторе выглядит криво.

Поставил (на макбук с ретиной, это не 4k-ноут на который жаловались, но достаточно близко), буду разбираться.

Но!

Выглядит криво там ПОЧТИ ВСЕ. Вот начиная с инсталлятора этой самой 8.1: что выбор раздела диска на который ставить, что ввод серийника, что вообще вся инсталляция до перезагрузки - все нужно рассматривать мелкоскопом.

Ну и дальше аналогично. Если программы самой MS еще нормально (хотя вот MS IE мелковат, на мой вкус), то практически все 3rd party - просто катастрофа местами.

То есть размеры диалогов то очень много где заданы жестко, шрифты - масштабируются, в результате все разваливается вообще нахрен. Или, наоборот, там где размер шрифта жестко задан - все выглядит в пропорцию, только ОЧЕНЬ МЕЛКОЕ.

Похоже что вот Apple, с его Logical Pixels/Physical Pixels (и первые - или 1x1 или 2x2 вторые) - поступил вот верно. Уж как минимум, старые, не Retina-aware, программы вели себя прилично. Ну и в подавляющем количестве мест (кроме битмепов и OpenGL) оно скейлилось как-то само, внутри.

А вот виндовая гибкость (возможность поставить произвольный масштаб) - разработчиков будет больно кусать. Оно и раньше было не подарок, но обычное увеличение было процентов 120, а на HiDPI экранах оно ближе к 200.

Ну и да, разнообразие убъет всех: 4k может быть и 15" и 32", понятно что "увеличение" на них сильно отличается. Как сохранить в такой ситуации UI (если временами его по пикселям подбираешь) - вот не знаю, да.

Q: windows file associations

Вот есть такой foobar2000. Там в настройках есть раздел Shell Integration, если его открыть, там есть линк 'Manage file type associations', который открывает Control Panel, уже открытую в нужном месте, в ассоциациях foobar2000:

Хочу такого же.

Дошел до:

control.exe /name Microsoft.DefaultPrograms /page PageDefaultProgram
Оно открывает почти нужное: там список всех программ, до нужного мне места осталось два клика:
...

Трудовые будни

Сложность окружающего мира продолжает пугать.

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

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

Запускаю инсталлятор, то, се, иконка RD на десктопе, даблкликаю на нее. И НИЧЕГО. Опаньки!

Еще кликаю. Опять ничего. Еще кликаю. Ничего.

Потом, когда я уже пригорюнился, раз и...

Продать козу

Почти четыре года я жил с git-сервером на https. Завести репозиторий - работа на сервере (у меня, правда, репозиториев мало). Работает - медленно. Авторизацию по сертификату сделать - можно, но опять работа на сервере.

Поставил gitolite.

Ощущение, что продал козу.

Минусы понятны, нужен работающий настоящий ssh на клиенте. Но git-msys (для Win32) приходит с ним, а на остальных системах оно и так есть.

Q: Apple Developer Certificate

Вот регистрируемся в Mac Developer Program за $99 (в год).

Идем туда, выписываем себе девелоперский сертификат.

Смотрим в него - а он на 5 лет (с момента выписки, не с начала действия программы)

Верно ли я понимаю, что если в последний день действия годовой оплаты Эпплу выписать себе сертификат, то можно поэкономить $495 на следующие 5 лет? Или отвалится чего?

Речь не про (макосный) App Store, а именно про 3rd-party софт, который раздается мимо аппстора.

Ненависти к Qt псто

А вот представим себе такое вот:

  • QGraphicsView и QGraphicsScene
  • Показываем картинку с каким-то увеличением, так что есть скроллбары (или только один)
  • И хотим показать следующую картинку "примерно так же" (в смысле относительного положения окна просмотра относительно всей картинки)
  • И хотим, чтобы когда мы вернулись к предыдущей - позиция сохранялась бы.
Нормальное желание, правда?

Ну вот казалось бы решение:

  • Рассчитать какая (относительная, в координатах 0-1) точка исходной картинки расположена в центре экрана.
  • Рассчитать далее, какая абсолютная точка следующей картинки должна быть в этом самом центре.
  • Дальше - view->centerOn()
Почти работает.

Почти - потому что есть скроллбары, они могут быть, они могут не быть, они могут быть у первой картинки, но не у второй (размеры другие) и наоборот. Скроллбары - меняют центр View (потому что занимают ширину).

И приходится нецензурно закатывать солнце вручную:

  • Если исходно скроллбар есть - запоминаем его относительную позицию (где у нас value() между minimum() и maximum()). Если нет - берем запомненную ранее, когда скроллбар был.
  • Дальше нужно посчитать, будет ли скроллбар при показе следующей (какой размер больше, картинки или viewport()
    • Если не будет, то скроллбар надо явно выключить, потому что если разница в несколько пикселов, то его может включить.
    • Если будет - то явно включить, доверять автоматике нельзя (бывает клево: view ставит скроллбары и зовет resizeEvent(). resizeEvent() знает, что режим у нас fit-to-screen и ресайзит картинку. view - видит что картинка поресайзилась, убирает скроллбары, зовет resizeEvent().....).
  • И если скроллбар будет - то явно ему поставить value (пересчитать из предыдущего относительного)
  • А если не будет - то припрятать относительное value до следующего раза, когда скроллбар появится.
Работает. Можно листать 10 картинок (разного размера и aspect ratio) вперед, потом назад - и вернуться ровно в ту точку/экранную позицию, с которой начинали.

Но, сука, 98 строк кода (считая пробелы). Вместо эдак 8-и, с которыми работает, но примерно.

Я прийшов, а тебя два!

В прошлой заметке я написал:
После этого начинаешь любить Win32 особенно остро.
Это продолжалось недолго, я познакомился с Registry Redirector в WOW64. В сочетании с тем, что в в Win7 оно отличается от более старых версий.

Я прийшов, тебе нема

Вот есть такой вызов, strnlen:

     size_t
     strnlen(const char *s, size_t maxlen);

DESCRIPTION
     The strnlen() function attempts to compute the length of s, but never scans beyond the
     first maxlen bytes of s.
И он есть, например, в Mac OS X 10.7 и новее.

Берем код с этим вызовом, собираем с -mmacosx-version-min=10.5 (должен получиться совместимый c 10.5 код, да?) на 10.8, несем на 10.6, запускаем.

Все падает.

И ладно бы падало с внятным сообщением, вот не могу залинковать такое. Нет, SIGSEGV, нулевой указатель (на функцию?).

После этого начинаешь любить Win32 особенно остро.

Q: Qt 5.2 и Retina

Граждане разработчики,

Особенно использующие Qt, вы же тут читаете, я знаю.

Вот есть Qt 5.2 и Маки с ретиной. Надо, значит, чтобы было Щастье.

На самом деле, 99% Щастья есть сходу: если в Info.plist написан NSPrincipalClass, то все стандартные элементы (шрифты, к примеру) рендерятся в ретину и все работает.

Но.

У меня используется свой OpenGL-код, который зовется из QGraphicsScene::drawBackground(). Он, понятно, сходу рисует в четверти окошка в таком случае.

Эксперименты показали, что достаточно поменять одну строчку. Вместо:

glViewport(x,y,w,h);
написать
int dpr = painter->device()->devicePixelRatio();
glViewport(x*dpr,y*dpr,w*dpr,h*dpr);
И все начинает (вроде бы?) работать как надо. Зумится, ротейтится, сдвигается - все вроде как надо.

Так вот, вопрос: Что Я Делаю Не Так?. Не упускаю ли я что-то важное?

P.S. Рассматривание кода Qt-шных примеров не дало ответа. А именно: в части примеров делают именно так. И все работает. А вот в qtbase\examples\widgets\graphicsview\boxes - вообще нет никакого следа devicePixelRatio(), все каким-то чудом работает "само" (и ретина поддерживается нормально)

mailto: URL Q (Mac)

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

Допустим, я хочу юзеру дать готовую ссылку в которую он кликает и получает в почтовой программе готовое к отсылке письмо.

Ну как-то так

<a href="mailto:bla@bla.com?subject=my%20subject&body=message%20body">Click here to open your mailer</a>
Все, типа, работает. Проверял на Windows+Thunderbird и на Маке с Mail.

В деталях все хуже. Конкретно, на маке.

У меня Qt 4.8 (а не браузер), раскопки показали, что все фигачится в результате в LSOpenCFURLRef().

Так вот, если в body письма больше одной строчки, то переводы строк эскейпить не надо, если поэскейпить, то письмо будет одной строкой, содержащей %0D%0A. Ну ладно, формируем URL прямо с переводами строк.

Главная беда наступает, если в текст письма нужно положить URL со своими & Если поэскейпить, то в тексте письма так и будет %26. Если не экскейпить, то на первом же & начнется, естественно, следующий аргумент.

Два вопроса

  1. Ну и кто они после этого?
  2. И что делать то? Не, мне несложно подпереть это дело мимо Qt (благо там есть возможность посадить свой handler на отдельную scheme), но как?

P.S. Проверял на 10.8, на 10.6 - вроде бы то же самое, другие не проверял.

Про QThread

Несколько дней писал массогабаритный макет многопоточной программы на Qt с использованием QThread

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

Имею сказать:

  • Классический способ с переопределением run(), сном там на QWaitCondition и передачей данных через отдельный вызов с блокировкой на QMutex - ужас. Очень жаль, что этот ужас до сих пор существует в документации. Насколько я понимаю, кстати, есть некоторый шанс пропустить очередное "задание на обработку" - если пока внутри run() работают работу, передача параметров случится несколько раз.
  • Описанный в документации же более правильный способ (он там описан первым) - еще более странный. Если обратили внимание, там есть workerThread в Worker, еще один workerThread в Controller и Worker передается в контроллерную workerThread ( worker->moveToThread(&workerThread);). Что этим хотели сказать - неясно.
  • Есть замечательный блог-пост You re doing it wrong , только как делать right - написано, на мой вкус, невнятно (а ссылка на старую запись - битая, впрочем вот верная, но и она помогает плохо.
  • О счастье, есть правильная идея (не в документации Qt): How To Really, Truly Use QThreads; The Full Explanation. Оно немножко недоделано под мой случай (т.к. предполагается, что Worker делает одну задачу и завершается), но в сочетании с вот этим текстом становится совсем хорошо (из последнего текста берем про QMetaObject::invokeMethod, чтобы не делать лишних connect(), которые нафиг не нужны).
Итого, приходим к следующему:

Pages

Subscribe to Программирование