swift - MTKView刷新问题

标签 swift gpu metal visual-artifacts mtkview

我正在通过 MTKView 合成一组 UIImage,我发现刷新问题仅在合成阶段出现,但一旦我与应用程序交互,这些问题就会消失。换句话说,复合 Material 按预期工作,但它们在屏幕上的外观看起来有点问题,直到我通过放大/翻译等强制刷新。

我发布了两个显示实际问题的视频:Glitch1 , Glitch2

我选择的复合方法是,我将每个 UIImage 转换为一个 MTLTexture,我将其提交给设置为“.load”的渲染缓冲区,渲染一个带有该纹理的多边形,然后我对每个图像重复该过程在 UIImage 数组中。

复合 Material 有效,但屏幕反馈,正如您从视频中看到的那样,非常有问题。

关于可能发生的事情有什么想法吗?任何建议将不胜感激

一些相关的代码:

for strokeDataCurrent in strokeDataArray {

        let strokeImage = UIImage(data: strokeDataCurrent.image)
        let strokeBbox = strokeDataCurrent.bbox
        let strokeType = strokeDataCurrent.strokeType
        self.brushStrokeMetal.drawStrokeImage(paintingViewMetal: self.canvasMetalViewPainting, strokeImage: strokeImage!, strokeBbox: strokeBbox, strokeType: strokeType)

} // end of for strokeDataCurrent in strokeDataArray

...

func drawStrokeUIImage (strokeUIImage: UIImage, strokeBbox: CGRect, strokeType: brushTypeMode) {

    // set up proper compositing mode fragmentFunction
    self.updateRenderPipeline(stampCompStyle: drawStampCompMode)

    let stampTexture = UIImageToMTLTexture(strokeUIImage: strokeUIImage)
    let stampColor = UIColor.white
    let stampCorners = self.stampSetVerticesFromBbox(bbox: strokeBbox)
    self.stampAppendToVertexBuffer(stampUse: stampUseMode.strokeBezier, stampCorners: stampCorners, stampColor: stampColor)
    self.renderStampSingle(stampTexture: stampTexture)


  } // end of func drawStrokeUIImage (strokeUIImage: UIImage, strokeBbox: CGRect)

func renderStampSingle(stampTexture: MTLTexture) {

    // this routine is designed to update metalDrawableTextureComposite one stroke at a time, taking into account
    // whatever compMode the stroke requires. Note that we copy the contents of metalDrawableTextureComposite to
    // self.currentDrawable!.texture because the goal will be to eventually display a resulting composite

    let renderPassDescriptorSingleStamp: MTLRenderPassDescriptor? = self.currentRenderPassDescriptor

    renderPassDescriptorSingleStamp?.colorAttachments[0].loadAction = .load
    renderPassDescriptorSingleStamp?.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0)
    renderPassDescriptorSingleStamp?.colorAttachments[0].texture = metalDrawableTextureComposite 

    // Create a new command buffer for each tessellation pass
    let commandBuffer: MTLCommandBuffer? = commandQueue.makeCommandBuffer()

    let renderCommandEncoder: MTLRenderCommandEncoder? = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptorSingleStamp!)

    renderCommandEncoder?.label = "Render Command Encoder"
    renderCommandEncoder?.setTriangleFillMode(.fill)
    defineCommandEncoder(
      renderCommandEncoder: renderCommandEncoder,
      vertexArrayStamps: vertexArrayStrokeStamps,
      metalTexture: stampTexture) // foreground sub-curve chunk

    renderCommandEncoder?.endEncoding() // finalize renderEncoder set up

    //begin presentsWithTransaction approach  (needed to better synchronize with Core Image scheduling
    copyTexture(buffer: commandBuffer!, from: metalDrawableTextureComposite, to: self.currentDrawable!.texture)
    commandBuffer?.commit() // commit and send task to gpu

    commandBuffer?.waitUntilScheduled()
    self.currentDrawable!.present()
    // end presentsWithTransaction approach
    self.initializeStampArray(stampUse: stampUseMode.strokeBezier) // clears out the stamp array in preparation of next draw call

  } // end of func renderStampSingle(stampTexture: MTLTexture)

最佳答案

首先,Metal 领域非常深,它在 MTKView 构造中的使用记录很少,特别是对于不属于更传统游戏范例的任何应用程序。在@warrenm、@ken-thomases 和@modj 等人的帮助下,我在 Metal 方面积累了有限的经验,这就是我发现自己的地方,他们的贡献对我和 Swift/Metal 社区都非常有值(value)在逃。非常感谢你们所有人。

其次,任何对 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 开发人员可能会在未来发现它有用。

关于swift - MTKView刷新问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56980853/

相关文章:

ios - Swift - 如何从语音保存文本文件

Swift - SpriteKit - 在永远运行时更新 SKAction.waitForDuration

python - 更新我的 NVIDIA 版本过时错误

objective-c - 在MetalKit应用程序中处理输入事件

ios - 加载时不显示 UITableViewCell.detailTextLabel

c++ - 如何在运行时在 OpenCL 中获取计算单元 ID?

multithreading - OpenMP 可以用于 GPU 吗?

ios - Metal 中的片段着色器和顶点着色器

ios - XCode Metal 模板 : Where is VertexAttribute. 位置定义了吗?

swift - 如何在 Swift 中读取 ANSI Escape 代码响应值?