Как напрямую обновлять пиксели - с помощью CGImage и прямого CGDataProvider

Актуальный вопрос

Несколько ответов решат мою проблему:

  1. Могу ли я заставить CGImage перезагружать свои данные из прямого поставщика данных (созданного с помощью CGDataProviderCreateDirect), как это делает CGContextDrawImage? Или есть другой способ настроить self.layer.contents для этого?
  2. Есть ли CGContext конфигурация или трюк, который я могу использовать для рендеринга изображений 1024x768 со скоростью не менее 30 кадров в секунду, согласованно с CGContextDrawImage.
  3. Кто-нибудь смог успешно использовать CVOpenGLESTextureCacheCreateTextureFromImage для обновления буфера в реальном времени с собственными данными текстуры? Я думаю, что моей самой большой проблемой является создание CVImageBuffer, поскольку я скопировал другие свойства из документации Apple для текстур. Если у кого-то есть дополнительная информация по этому поводу, это было бы здорово.
  4. Любые другие рекомендации о том, как я могу получить изображение из памяти на экране со скоростью 30 кадров в секунду.

Фон (лоты):

Я работаю над проектом, в котором мне нужно изменить пиксели данных изображения NPOT в реальном времени (минимум 30 кадров в секунду) и нарисовать их на экране в iOS.

Моя первая мысль заключалась в том, чтобы использовать OpenGL с glTexSubimage2D для обновления, но, к сожалению, это оказалось очень медленным (6 кадров в секунду на iPad), поскольку драйвер переключает и преобразует мои данные RGB каждый кадр в BGR. Так что отправьте это в BGR, вы говорите, и я тоже, но по какой-то причине вы не можете вызвать glTexSubImage2D с GL_BGR go figure. Я знаю, что некоторая медлительность связана с тем, что это не мощность двух данных изображения, но мои требования диктуют это.

Дальнейшее чтение привело меня к CVOpenGLESTextureCacheCreateTextureFromImage, но во всех примерах используется прямой ввод с камеры для получения CVImageBufferRef. Я пытался использовать документацию (не официальную, но только комментарии к заголовку), чтобы создать собственный CVImageBuffer из данных моего изображения, но он не работал с этим (нет ошибок, просто пустая текстура в отладчике), что заставляет меня думать, что Apple построила это специально для обработки данных камеры в реальном времени, и это не тестировалось за пределами этой области, кроме IDK.

В любом случае, отказавшись от своего достоинства, сбросив OpenGL и переключив свои мысли на CoreGraphics, я пришел к этому вопросу самый быстрый способ нарисовать экранный буфер на iphone, который рекомендует использовать CGImage, поддерживаемый CGDataProviderCreateDirect, который позволяет вам возвращать указатель на данные изображения, когда это необходимо CGImage, прекрасно, верно ? Ну, похоже, это не совсем так, как рекламируется. Если я использую CGContextDrawImage, то все работает. Я могу изменить буфер пикселей, и каждый розыгрыш, он запрашивает данные изображения у моего поставщика данных, как и должен, вызывая методы в CGDataProviderDirectCallbacks (Note: они, похоже, имеют встроенную оптимизацию, игнорируют обновленный указатель, если он имеет тот же адрес как предыдущий). CGContextDrawImage не очень быстрый (около 18 кадров в секунду) даже с отключением интерполяции, которое увеличило скорость примерно до 6 кадров в секунду. В документации Apple говорится, что использование self.layer.contents будет намного быстрее, чем CGContextDrawImage. Использование self.layer.contents работает для первого назначения, но CGImage никогда не запрашивает перезагрузку у поставщика данных, как это делает CGContextDrawImage, даже когда я вызываю [layer setNeedsDisplay]. В вопросе SO, на который я ссылался, пользователь показывает свое решение проблемы, создавая и уничтожая новое изображение CGImage из источника данных в каждом кадре, безнадежно медленный процесс (да, я пробовал), так что время для настоящего вопроса.

Примечание. Я профилировал все эти операции и знаю, что проблема действительно glTexSubImage для OpenGL, а CGContextDrawImage действительно проблема от CoreGraphics, поэтому никаких ответов "перейти в профиль" нет.

ИЗМЕНИТЬ. Исходный код, демонстрирующий эту технику, теперь можно найти по адресу http://github.com/narpas/image-sequence-streaming


person Justin Meiners    schedule 29.08.2012    source источник
comment
Что касается 1), нет, CGImageRef неизменяемы. Конечно, вы можете создать новый CGImageRef, используя измененный поставщик. Во 2) какой сейчас fps? Кроме того, разве вам не нужно беспокоиться о сетчатке - она ​​будет в 2 раза больше ... Раньше я создавал фильм, используя AV-фреймворк и неподвижные изображения (снимки экрана), и я думаю, что получил 20-30 fps на Mac. Идея состоит в том, чтобы создать сжатый видеообъект, который, скорее всего, получит аппаратную помощь при воспроизведении. Попытка увеличить количество пикселей, которое вы хотите, будет проблематичной, но я понятия не имею об абсолютных границах производительности.   -  person David H    schedule 30.08.2012
comment
Каков источник этих изображений? У меня есть код для использования кешей текстур с источниками фильмов: stackoverflow.com/a/10656390/19679, если это что ты хочешь здесь заняться. Я также сделал это с необработанными входными байтами, поэтому кеши текстур будут работать для них, но я не могу найти свой код для этого прямо сейчас. Я действительно помню, что кеши мало что приносят вам за загрузку одного изображения, но они выигрывают для множества обновленных кадров подряд.   -  person Brad Larson    schedule 30.08.2012
comment
@DavidH, как я уже упоминал, хотя с CGContextDrawImage CGImage обновляется, поэтому внутри каким-то образом должен быть способ перезагрузить его, только с self.layer.contents, который я не могу заставить его перезагрузить.   -  person Justin Meiners    schedule 30.08.2012
comment
@BradLarson В вашем коде материал AVAsset создает для вас CVImageBufferRef. Мне нужно создать его вручную с помощью CVPixelBufferCreate или чего-то подобного, и я думаю, где моя ошибка. Мои данные - это просто необработанный массив пикселей RGB, который обычно использует openGL, поэтому мне нужно каким-то образом передать это в CVImageBufferRef, который нравится кешу. Я обязательно попробую некоторые другие вещи, которые я видел в вашем коде, хотя   -  person Justin Meiners    schedule 30.08.2012
comment
@DavidH 2. 18 кадров в секунду с изображениями 1024x768 с использованием CGContextDrawImage, упомянутого в разделе фона   -  person Justin Meiners    schedule 30.08.2012
comment
Я просто прочитал все комментарии в вашей ссылке выше (`` самый быстрый способ ... '') - в конце есть комментарий об использовании ДВУХ КАЛИБРОВ, каждый из которых подкреплен изображением, и, по сути, вы предоставляете один, в то время как он показывает, переключает резервное изображение другого, переключателя и т. д. Вы не можете обновить поставщика данных и получить тот же самый CGImageRef, который вы уже использовали для перерисовки себя - CALayer, вероятно, уже кэшировал свои данные. Если вы переключаете изображения, он должен повторно получить данные из нового изображения. Возможно, если вы отключите CGImageRef, а затем установите его снова, вы сможете заставить CALayer перезагрузить одно изображение.   -  person David H    schedule 30.08.2012
comment
@DavidH да, я тоже так думал, я пробовал это с двумя буферами CGImageRef, меняющими местами каждый кадр, но не повезло ... Это полностью кеш, потому что если я создаю CGImage с поставщиком данных, чем модифицирую пиксели, чем назначу себя .layer.contents, изображение будет из измененного буфера, я просто не могу его обновить после назначения   -  person Justin Meiners    schedule 30.08.2012
comment
Что касается кешей текстур, вам понадобится что-то вроде описанного здесь: allmybrain.com/2011/12/08/ только вместо того, чтобы связывать эту текстуру в качестве вывода FBO, просто используйте ее в качестве текстуры. Затем вы можете напрямую изменить байты CVPixelBufferRef и просто повторно визуализировать вашу сцену, не полагаясь на glTexSubimage2D. Однако одно предостережение заключается в том, что внутренний байтовый формат текстуры - это BGRA, поэтому вам все равно понадобится операция swizzling в вашем шейдере в качестве первого шага. Однако это действительно быстро.   -  person Brad Larson    schedule 30.08.2012
comment
@BradLarson, спасибо, взгляни. Меня не беспокоит вещь BGRA   -  person Justin Meiners    schedule 31.08.2012
comment
@BradLarson, который работал, и я смог заставить работать CoreVideo openGL, к сожалению, похоже, что это действительно не помогает, я получаю ту же частоту кадров, и профиль показывает, что он просто вызывает glTexSubimage2D, который мне вообще не помогает, любая информация на этом? Мне пришлось вызывать CVOpenGLESTextureCacheCreateTextureFromImage каждый кадр, изменение буфера пикселей не приводит к обновлению текстуры ..   -  person Justin Meiners    schedule 31.08.2012
comment
@BradLarson Только что протестировал, работает на устройстве, а не на симуляторе, большое спасибо (вероятно, это связано с разделением RAM / VRAM на Mac). Фактическое исправление заключалось в создании словаря атрибутов, который сделал его поддерживаемым IOSurfaced (из примера кода, который вы мне дали)   -  person Justin Meiners    schedule 31.08.2012
comment
Симулятор не работает, когда дело доходит до кешей текстур. Я использую определения компилятора, чтобы обойти это в моем коде, возвращаясь к glTexImage2D () и т.п. Что касается текстуры, которая не обновляется, я помню, что это как-то связано с настройкой IOSurface, но я не могу найти код, который я использовал, чтобы исправить это.   -  person Brad Larson    schedule 31.08.2012
comment
Есть ли обновления по этому вопросу с учетом новых графических API в macOS?   -  person Rol    schedule 03.02.2021
comment
@Rol В наши дни я не участвую в яблочной экосистеме. Вы имеете в виду отказ от opengl в пользу металла? Или другие изменения CoreGraphics?   -  person Justin Meiners    schedule 04.02.2021


Ответы (1)


Спасибо Брэду Ларсону и Дэвиду Х за помощь с этим (см. Наше полное обсуждение в комментариях). Оказалось, что использование OpenGL и CoreVideo с CVOpenGLESTextureCache оказалось самым быстрым способом вывода необработанных изображений на экран (я знал, что CoreGraphics не может быть самым быстрым!), Давая мне 60 кадров в секунду с полноэкранными изображениями 1024x768 на iPad 1. Их мало. документацию по этому поводу, поэтому я постараюсь объяснить как можно больше, чтобы помочь людям:

CVOpenGLESTextureCacheCreateTextureFromImage позволяет вам создавать текстуру OpenGL, память которой напрямую отображается на CVImageBuffer, который вы используете для ее создания. Это позволяет вам создать, скажем, CVPixelBuffer с вашими необработанными данными и изменить указатель данных, собранный из CVPixelBufferGetBaseAddress. Это дает вам мгновенные результаты в OpenGL без необходимости изменять или повторно загружать фактическую текстуру. Просто не забудьте заблокировать с помощью CVPixelBufferLockBaseAddress перед изменением пикселей и разблокировать, когда закончите. Обратите внимание: в настоящее время это не работает в симуляторе iOS, только на устройстве, которое, как я предполагаю, относится к подразделению VRAM / RAM, где у ЦП нет прямого доступа к VRAM. Брэд рекомендовал использовать условную проверку компилятора для переключения между необработанными glTexImage2D обновлениями и использованием кешей текстур.

Несколько вещей, на которые следует обратить внимание (из-за их комбинации это не сработало для меня):

  1. Тест на устройстве
  2. Убедитесь, что ваша CVPixelBuffer поддерживается kCVPixelBufferIOSurfacePropertiesKey см. ссылка например (еще раз спасибо, Брэд).
  3. Вы должны использовать GL_CLAMP_TO_EDGE для данных текстуры NPOT с OpenGL ES 2.0
  4. Связывайте кеши текстур с glBindTexture(CVOpenGLESTextureGetTarget(_cvTexture), CVOpenGLESTextureGetName(_cvTexture));, не будь глупцом, как я, и используйте CVOpenGLESTextureGetTarget для обоих параметров.
  5. Не воссоздайте текстуру в каждом кадре, просто скопируйте данные изображения в указатель, полученный от CVPixelBufferGetBaseAddress, чтобы обновить текстуру.
person Justin Meiners    schedule 01.09.2012
comment
Я думаю, что это CVOpenGLESTextureCacheCreateTextureFromImage, а не CGOpenGLESTextureCacheCreateTextureFromImage, в противном случае - хорошо! - person MickyD; 07.09.2012
comment
+1 спасибо, приятель, это сработало. Также могу подтвердить, что нам не нужно создавать текстуры каждый раз, когда мы только что запустили наш проект. Мы также не вызываем CVOpenGLESTextureCacheFlush, как это делают образцы Apple. Полностью согласен с повторной запутанной документацией. Еще раз спасибо - person MickyD; 11.09.2012
comment
@MickyDuncan есть идеи, что на самом деле делает CVOpenGLESTextureCacheFlush? Документация расплывчата, и я не заметил влияния на память с ней или без нее. - person Justin Meiners; 12.09.2012
comment
То же самое и здесь, приборы вроде бы ничего не указали. Может быть, это повлияет больше, если мы будем снимать видео? - person MickyD; 12.09.2012
comment
@JustinMeiners Можно ли опубликовать настоящий фрагмент кода? Мне сложно заставить это работать ... - person anna; 03.02.2013
comment
@anna Я на самом деле скоро открою для себя что-нибудь с этим, я дам тебе подумать, когда он будет готов. - person Justin Meiners; 20.02.2013
comment
@JustinMeiners Спасибо за ответ. Это было бы круто. Я с нетерпением жду. - person anna; 23.02.2013
comment
@JustinMeiners Вы уже что-то открывали? Где скачать? :) - person OMH; 14.06.2013
comment
@OHM это не закончено, но я постараюсь что-нибудь убрать завтра или в воскресенье. - person Justin Meiners; 15.06.2013
comment
@JustinMeiners Спасибо! У тебя есть гитхаб что ли? - person OMH; 02.07.2013
comment
@OMH все еще работает над этим - я дам вам знать, как только это произойдет (надеюсь, скоро?) - person Justin Meiners; 02.07.2013
comment
@OMH Спасибо за терпеливое ожидание (и за мотивацию закончить), у меня есть проект, опубликованный на github. com / narpas / image-sequence-streaming Источник не зафиксирован, но должен появиться в течение следующего дня. - person Justin Meiners; 03.07.2013
comment
Это 7-е. whip crack =) Не могли бы вы хотя бы опубликовать отрывок из вышеизложенного? - person aoakenfo; 08.07.2013
comment
@aoakenfo, ха-ха, спасибо за настойчивость - фактический код готов и готов к отправке - один из наборов данных, который я использую в качестве примера, был использован для проекта, который я выполнял для другой компании. Я не могу использовать это, поскольку он принадлежит им - я создаю данные для замены. - person Justin Meiners; 08.07.2013
comment
@aoakenfo сейчас загружено - person Justin Meiners; 20.07.2013