0

我正在开发一个 MTKView 支持的绘画程序,该程序可以通过存储关键帧的 MTLTextures 数组重放绘画历史。我遇到了一个问题,有时这些 MTLTextures 的内容会被打乱。

例如,假设我想将下面的一部分图形存储为关键帧:

关键帧创建之前

在播放过程中,有时绘图会完全按照预期显示,但有时会显示如下:

在关键帧播放期间

注意图片的扭曲部分。(未失真部分构成静态背景图像,它不是相关关键帧的一部分)

我在下面描述了我从 MTKView 的 currentDrawable 创建单个 MTLTextures 的方式。由于我不会讨论颜色深度问题,这个过程可能看起来有点迂回。

我首先得到构成关键帧的屏幕子部分的 CGImage。
我使用该 CGImage 创建与 MTKView 设备相关的 MTLTexture。我将该 MTLTexture 存储到一个 MTLTextureStructure 中,该 MTLTextureStructure 存储 MTLTexture 和关键帧的边界框(稍后我将需要它)最后,我存储在 MTLTextureStructures 数组(keyframeMetalArray)中。在播放过程中,当我点击一个关键帧时,我会从这个 keyframeMetalArray 中获取它。

相关代码概述如下。

let keyframeCGImage = weakSelf!.canvasMetalViewPainting.mtlTextureToCGImage(bbox: keyframeBbox, copyMode: copyTextureMode.textureKeyframe) // convert from MetalTexture to CGImage

let keyframeMTLTexture = weakSelf!.canvasMetalViewPainting.CGImageToMTLTexture(cgImage: keyframeCGImage)

let keyframeMTLTextureStruc = mtlTextureStructure(texture: keyframeMTLTexture, bbox: keyframeBbox, strokeType: brushTypeMode.brush)

weakSelf!.keyframeMetalArray.append(keyframeMTLTextureStruc)

在不提供有关每次转换如何发生的细节的情况下,我想知道从架构设计的角度来看,我是否忽略了一些正在破坏我存储在 keyframeMetalArray 中的数据的东西。尝试将这些 MTLTextures 存储在 volatile 数组中可能是不明智的,但我不知道这一点。我只是认为使用 MTLTextures 将是更新内容的最快方法。

顺便说一句,当我将关键帧数组换成 UIImage.pngData 数组时,我没有显示问题,但速度要慢得多。从好的方面来说,它告诉我从 currentDrawable 到 keyframeCGImage 的初始捕获工作正常。

任何想法将不胜感激。

ps根据反馈添加一些细节:

mtlTextureToCGImage:

func mtlTextureToCGImage(bbox: CGRect, copyMode: copyTextureMode) -> CGImage {

    let kciOptions = [convertFromCIContextOption(CIContextOption.outputPremultiplied): true,
                      convertFromCIContextOption(CIContextOption.useSoftwareRenderer): false] as [String : Any]
    let bboxStrokeScaledFlippedY = CGRect(x: (bbox.origin.x * self.viewContentScaleFactor), y: ((self.viewBounds.height - bbox.origin.y - bbox.height) * self.viewContentScaleFactor), width: (bbox.width * self.viewContentScaleFactor), height: (bbox.height * self.viewContentScaleFactor))

let strokeCIImage = CIImage(mtlTexture: metalDrawableTextureKeyframe,
                                  options: convertToOptionalCIImageOptionDictionary(kciOptions))!.oriented(CGImagePropertyOrientation.downMirrored)
      let imageCropCG = cicontext.createCGImage(strokeCIImage, from: bboxStrokeScaledFlippedY, format: CIFormat.RGBA8, colorSpace: colorSpaceGenericRGBLinear)

      cicontext.clearCaches()

      return imageCropCG!

} // end of func mtlTextureToCGImage(bbox: CGRect)

CGImageToMTL纹理:

func CGImageToMTLTexture (cgImage: CGImage) -> MTLTexture {

    // Note that we forego the more direct method of creating stampTexture:
    //let stampTexture = try! MTKTextureLoader(device: self.device!).newTexture(cgImage: strokeUIImage.cgImage!, options: nil)
    // because  MTKTextureLoader seems to be doing additional processing which messes with the resulting texture/colorspace

    let width = Int(cgImage.width)
    let height = Int(cgImage.height)


    let bytesPerPixel = 4

    let rowBytes = width * bytesPerPixel
    //
    let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm,
                                                                 width: width,
                                                                 height: height,
                                                                 mipmapped: false)
    texDescriptor.usage = MTLTextureUsage(rawValue: MTLTextureUsage.shaderRead.rawValue)
    texDescriptor.storageMode = .shared
    guard let stampTexture = device!.makeTexture(descriptor: texDescriptor) else {
      return brushTextureSquare // return SOMETHING

    }

    let dstData: CFData = (cgImage.dataProvider!.data)!
    let pixelData = CFDataGetBytePtr(dstData)

    let region = MTLRegionMake2D(0, 0, width, height)

    print ("[MetalViewPainting]: w= \(width) | h= \(height)  region = \(region.size)")

    stampTexture.replace(region: region, mipmapLevel: 0, withBytes: pixelData!, bytesPerRow: Int(rowBytes))

    return stampTexture

  } // end of func CGImageToMTLTexture (cgImage: CGImage)
4

1 回答 1

2

失真的类型看起来像是 CGImage 和 MTLTexture 之间的每行字节对齐问题。当您的图像大小超出 MTLDevice 的每行字节数对齐要求时,您可能只会看到此问题。如果您确实需要将纹理存储为 CGImage,请确保在复制回纹理时使用 CGImage 的 bytesPerRow 值。

于 2019-08-29T18:29:40.050 回答