Требуется ли рисование в MTKView или CAMetalLayer в основном потоке?

Хорошо известно, что обновление пользовательского интерфейса в AppKit или UIKit требуется для выполнения в основном потоке. Есть ли у Metal такое же требование, когда дело доходит до представления drawable?

В NSView, размещенном на уровне, с которым я играл, я заметил, что могу вызывать [CAMetalLayer nextDrawable] из dispatch_queue, который не является main_queue. Затем я могу обновить текстуру этого чертежа, как обычно, и представить ее.

Это похоже работает правильно, но я нахожу это довольно подозрительным. Если я не пропустил что-то в документации, я не могу найти упоминания о требованиях к основному потоку Metal (либо за, либо против).

(Я тестирую на macOS 10.13, но полагаю, что основные требования к потоку будут такими же и для iOS...?)


person kennyc    schedule 12.08.2018    source источник


Ответы (1)


Безопасно рисовать на фоновых нитях. В документах для -nextDrawable говорится:

Вызов этого метода блокирует текущий поток ЦП до тех пор, пока не будет доступен новый объект рисования.

(Выделение добавлено.) Если бы его можно было вызывать только в основном потоке, это, вероятно, не было бы таким обобщенным. Кроме того, общий совет Apple состоит в том, чтобы избегать блокировки основного потока, поэтому вы можете подумать, что они каким-то образом укажут на этот факт здесь, например, посоветуют вам не вызывать его, если вы не уверены, что он не заблокируется.

Что касается того, как используется (а не получен) объект рисования, обратите внимание, что типичным вариантом использования является вызов метода -presentDrawable: командного буфера. Этот метод удобен для добавления запланированного блока обработчика (например, через -addScheduledHandler:), который затем вызовет -present на чертеже. Не указано, в каком потоке или очереди будут вызываться блоки обработчика, что говорит о том, что нет никаких обещаний, что вызов -present для drawable произойдет в основном потоке.

И даже после этого фактическое представление рисуемого на экране не синхронно вызову -present. Drawable ждет, пока любые команды, которые визуализируют или записывают его текстуру, не будут завершены, и только после этого представляются на экран. Не указано, как достигается эта асинхронность, но далее предполагается, что не имеет значения, в каком потоке -present вызывается.

Многопоточность немного обсуждается в Руководство по программированию на Metal, хотя оно и не такое прямолинейное, как можно было бы надеяться. См., в частности, раздел о нескольких потоках, буферах команд и кодировщиках команд. Обратите внимание, что обсуждается заполнение буферов команд фоновыми потоками и нет конкретных предупреждений о работе с чертежами. Опять же, это своего рода аргумент из-за отсутствия доказательств, но я думаю, что это ясно. Они заявляют, что только один поток может одновременно воздействовать на данный буфер команд, поэтому они рассматривают вопросы безопасности потоков.

person Ken Thomases    schedule 13.08.2018
comment
Я склонен согласиться, даже если это противоречит моим многолетним правилам AppKit. Цикл автоматического отображения MTKView просто вызывает drawRect, что действительно происходит в основном потоке. Если я запускаю свой собственный цикл рендеринга и вывожу его в CAMetalLayer, который может быть размещен в NSView, похоже, что я могу рендерить в nextDrawable этого металлического слоя без необходимости синхронизации с основным потоком. И наоборот, как бы вы заставили Metal гарантировать, что представленная в данный момент текстура находится в синхронизации с пользовательским интерфейсом? (Например, когда содержимое слоя должно точно отражать положение перетаскивания мышью.) - person kennyc; 13.08.2018
comment
По этому вопросу см. документы для -[CAMetalLayer presentsWithTransaction]. Установите значение true, не вызывайте -[MTLCommandBuffer presentDrawable:], вместо этого вызовите -waitUntilScheduled, а затем -[MTLDrawable present]. В этом случае вам придется сделать это в основном потоке, чтобы синхронизироваться с его CATransaction. - person Ken Thomases; 13.08.2018
comment
Ах, я делал части waitUntilScheduled и present, но не часть presentsWithTransaction. Я добавлю это и немного поиграю. - person kennyc; 13.08.2018