Доступ к пикселям текстуры в Metal

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

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

С другой стороны, Apple таки приближается к Swift 5, замораживает синтаксис и объявляет совместимость по ABI. И хотя Objc или C++ реализации приложений на Metal все еще субъективно шустрее чем Swift, а код не нужно переписывать от версии к версии практически с нуля, Swift начинает выглядеть как штука, на которой уже можно писать примеры для важных постов без вопросов к версии  XCode для сборки.

А еще мне хочется проверить работу шорткода [Gist] вместо кривого и дурацкого  [Code]. Ну вы поняли.

Сегодня не будет математики

До сих пор с помощью Метал мы решали практические задачи требующие больших вычислений. Как правило из предметной и очень прикладной области. Однако есть бооольшой класс проблем (в хорошем смысле этого слова) сугубо технического характера: без этих ваших формул. Вот, к примеру, совсем недавно поступил вопрос: а как прочитать цвет конкретных пикселей текстуры? Т.е. казалось бы простая и очевидная вещь, но вызывает трудность. Поэтому я пошарился по докам Apple, стековерфло и оказалось, что действительно вопрос не раскрыт — ибо для разработчика SDK это самоочевидно, но новичку не доступно.

Давайте сконструируем простое приложение

Приложение будет давать возможность показать в окошке картинку из файла. Мы сможем тыкнуть мышкой в картинку, нарисовать квадратную область, и определить средний цвет пикселов этой квадратной области. Размер области может быть и 1 пиксел, например. Но мы в коде зададим квадрат 20×20.

Традиционно исходники приложения в папочке ImageMetalling-14 если текст поста читать лень. Сборка как всегда с cocoapods:

$ pod install
$ open ImageMetalling-14.xcworkspace

И дальше в XCode запускаем проект.

Что под капотом?

Давайте начнем с того, что хостовый слой Metal предоставляет нам несколько базовых структур данных для взаимодействия с ядрами GPU: MTLTexture и MTLBuffer. По сути это обертки кусков памяти, которые мы можем размещать в областях CPU и GPU одновременно или копировать между различными типами памяти в контексте pipeline обработки. В зависимости от типа устройства при размещении данных в этих кусках нам нужно будет синхронизовать контекст с текущим тредом исполнения в CPU или нет. Но если опустить детали, то обе структуры — это просто последовательность данных, которую мы можем читать и изменять как в хостовом треде исполняемом на CPU так и на конкретном ядре GPU. Если мы что-то записали в текстуру, передали в ядро и произвели вычисления, то мы можем результаты вычислений разместить в другой кусок памяти, на который может быть линейно отображена произвольная структура. Вычисления могут быть записаны в результирующую текстуру, а может и в специальный объект.

По сути, MTLTexture отличается от MTLBuffer только тем, что текстура уже имеет специфический набор методов по доступу к её специально-структурированным данным зависящим от типа представления пикселей и семплов, а MTLBuffer — может содержать пользовательский тип данных с нужным разработчику набором полей и методов (ну и + какой-то заданный набор уже есть в обоих представлениях CPU и GPU).

Ну, мы так и поступим возьмем центр области, пробежимся по окружающим пикселям, сложим да поделим. А результат разместим в буфер с данными типа float3. Гениально же!

А, ну да, запустим столько ядер сколько нам нужно областей посчитать. Для задачи нам достаточно одного. Хотя это и из пушки по воробьям, но нам ехать, а не о культуре оптимизации.

Реализация ядра

Усреднение цвета

Boilerplate и вот это все в Metal

Тут должен быть TL;DR про то, что на чистом Metal кодить хоть и сильно удобнее по сравнению с OpenGL и даже OpenCL, да и Cuda иногда дает прикурить. Но для задач писания процессинга изображений все еще нужно кричать много слов и складывать кучу букв в осмысленный код. Поэтому для себя у нас есть обёрточка уменьшающая количество страдания и клавиатурной боли. Как полагается, в общем.

  • SDK IMProcessing — обертка для классов работы с фильтрами и их конструированием для нужд процессинга и анализа изображений;
  • SDK IMProcessingUI — обертка для объектов рендеринга на экран результатов фильтрации и анализа изображений.

IMProcessing скрывает много работы по созданию ядер фильтров, анализаторов, детекторов и т.п. А так же предоставляет возможность все процессы процессинга асинхронно параллелить и по максимуму давать возможность разогнать вентиляторы ваших макбуков. Но по сути — это всё тот же Metal.

Поэтому рассмотрим теперь как нам запустить ядро в работу и прочитать таки наши пикселы.

Передаем параметры в кернел:

Читаем буфер с цветами в массив:

Полный кода класса:  ColorObserver.swift. Он же в исходном SDK IMPColorObserver.

Отрисовка цвета в окошке приложения

Тут мы просто ловим факт асинхронного завершения исполнения процессинга на GPU и отрисовываем результат уже традиционными средствами OSX.

 

Выводы

Бок есть! И он либо правый либо левый. Но иногда надо посмотреть снизу.

 


Авторы блога не преследуют целей быть предельно корректным, но если заметили явную ашипку, если написали явную глупость, если что-то не понятно, или есть идеи улучшения: комментируйте или пишите на: imagemetalling [*] gmail.com.

Реклама

Доступ к пикселям текстуры в Metal: Один комментарий

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s