Прежде всего, домен Metal очень глубокий, и его использование в конструкции MTKView редко документируется, особенно для любых приложений, которые выходят за рамки более традиционной игровой парадигмы.Вот где я оказался в ограниченном опыте, который я накопил с Metal с помощью таких людей, как @warrenm, @ ken-thomases и @modj, чей вклад был очень ценным для меня, и для сообщества Swift / Metalв целомИтак, большое спасибо всем вам.
Во-вторых, всем, кто занимается поиском и устранением неисправностей металла, обратите внимание на следующее: Если вы получаете сообщение:
[CAMetalLayerDrawable present] should not be called after already presenting this drawable. Get a nextDrawable instead
, пожалуйста, неигнорируй это.Это может показаться достаточно безобидным, особенно если об этом сообщают только один раз.Но имейте в виду, что это признак того, что часть вашей реализации имеет недостатки и должна быть устранена, прежде чем вы сможете устранить любые другие связанные с Metal аспекты вашего приложения.По крайней мере, это был случай для меня.Как вы можете видеть из видео постов, симптомы этой проблемы были довольно серьезными и вызвали непредсказуемое поведение, из-за которого мне было трудно определить источник.Мне было особенно трудно понять, что я получил это сообщение ОДИН РАЗ в начале цикла приложения, но этого единственного экземпляра было достаточно, чтобы все остальное графически вышло из строя, как я думал, что это связано с CoreImage и /или другие совершенно несвязанные дизайнерские решения, которые я сделал.
Итак, как я избавился от этого предупреждения?Ну, в моем случае, я предполагал, что наличие настроек:
self.enableSetNeedsDisplay = true // needed so we can call setNeedsDisplay() to force a display update as soon as metal deems possible
self.isPaused = true // needed so the draw() loop does not get called once/fps
self.presentsWithTransaction = true // for better synchronization with CoreImage (such as simultaneously turning on a layer while also clearing MTKView)
означало, что я мог в значительной степени вызывать currentDrawable!.present()
или commandBuffer.presentDrawable(view.currentDrawable)
напрямую, когда бы я хотел обновить экран.Ну, это совсем не так.Оказывается, эти вызовы должны выполняться только в цикле draw () и доступны только через вызов setNeedsDisplay()
.После того, как я внес это изменение, я уже был на пути к разгадке загадки обновления.
Кроме того, я обнаружил, что настройка MTKView
self.isPaused = true
(так что я мог делать setNeedsDisplay()
вызовы напрямую) по-прежнемупривело к неожиданному поведению.Так что вместо этого я согласился:
self.enableSetNeedsDisplay = false // needed so we can call setNeedsDisplay() to force a display update as soon as metal deems possible
self.isPaused = false // draw() loop gets called once/fps
self.presentsWithTransaction = true // for better synchronization with CoreImage
, а также изменил цикл draw (), чтобы определить, какие обновления выполнять после установки флага metalDrawableDriver
И вызова setNeedsDisplay()
:
override func draw(_ rect: CGRect) {
autoreleasepool(invoking: { () -> () in
switch metalDrawableDriver {
case stampRenderMode.canvasRenderNoVisualUpdates:
return
case stampRenderMode.canvasRenderClearAll:
renderClearCanvas()
case stampRenderMode.canvasRenderPreComputedComposite:
renderPreComputedComposite()
case stampRenderMode.canvasRenderStampArraySubCurve:
renderSubCurveArray()
} // end of switch metalDrawableDriver
}) // end of autoreleasepool
} // end of draw()
Это может показаться многозначительным, но это был единственный механизм, который я нашел, чтобы получать согласованные обновления дисплея, управляемые пользователями.
Я надеюсь, что этот пост описывает без ошибоки жизнеспособное решение, которое разработчики Metal могут найти полезным в будущем.