Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/grails/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift MTKView刷新问题_Swift_Gpu_Metal_Visual Artifacts_Mtkview - Fatal编程技术网

Swift MTKView刷新问题

Swift MTKView刷新问题,swift,gpu,metal,visual-artifacts,mtkview,Swift,Gpu,Metal,Visual Artifacts,Mtkview,我正在通过MTKView合成一组UIImage,我看到刷新问题只在合成阶段出现,但在我与应用程序交互时就会消失。换言之,这些合成物工作正常,但在我通过放大/转换等方式强制刷新之前,它们在屏幕上的外观看起来有些浮躁 我发布了两个视频,展示了问题的实际情况: 我选择的合成方法是,我将每个UIImage转换为MTLTexture,并将其提交到渲染缓冲区集“.load”,该缓冲区渲染带有此纹理的多边形,然后我对UIImage数组中的每个图像重复该过程 合成效果很好,但从视频中可以看出,屏幕反馈非常容易出

我正在通过MTKView合成一组UIImage,我看到刷新问题只在合成阶段出现,但在我与应用程序交互时就会消失。换言之,这些合成物工作正常,但在我通过放大/转换等方式强制刷新之前,它们在屏幕上的外观看起来有些浮躁

我发布了两个视频,展示了问题的实际情况:

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

合成效果很好,但从视频中可以看出,屏幕反馈非常容易出错

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

一些相关代码:

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)

首先,领域金属非常深入,MTKView结构中的使用很少有文档记录,特别是对于任何不属于更传统游戏范式的应用程序。在@warrenm、@ken thomases和@modj等人的帮助下,我在金属方面积累了有限的经验,他们的贡献对我和整个Swift/金属社区都非常宝贵。向你们所有人表示深深的感谢

第二,对于任何对金属进行故障排除的人,请注意以下几点:如果您收到信息:

[CAMetalLayerDrawable present] should not be called after already presenting this drawable. Get a nextDrawable instead
请不要忽视它。这似乎是无害的,特别是如果它只被报告一次的话。但请注意,这表明您的实现有一部分存在缺陷,必须在解决应用程序的任何其他金属相关方面的故障之前解决。至少对我来说是这样。正如你从视频帖子中看到的,这个问题的症状相当严重,并导致了不可预测的行为,我很难找到问题的根源。对我来说,尤其难以理解的是,我在应用程序开发周期的早期只收到过一次这条消息,但这一个实例足以让其他一切在图形上变得不正常,我认为这可以归因于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()