Rendering 使用带MTLTexture的Carender在屏幕外渲染动画CALayer

Rendering 使用带MTLTexture的Carender在屏幕外渲染动画CALayer,rendering,metal,core-image,quartz-core,off-screen,Rendering,Metal,Core Image,Quartz Core,Off Screen,我想将动画的NSView(或只是底层的CALayer)渲染成一系列图像,而不在屏幕上显示视图。我想到了如何使用carender和MTLTexture实现这一点,但下面的方法存在一些问题 它在游乐场中运行,并将输出存储到下载中的屏幕外渲染文件夹: 导入应用工具包 进口金属 进口石英砂 导入PlaygroundSupport let view=NSView(帧:CGRect(x:0,y:0,宽度:600,高度:400)) 让圆=NSView(帧:CGRect(x:0,y:0,宽度:50,高度:50)

我想将动画的
NSView
(或只是底层的
CALayer
)渲染成一系列图像,而不在屏幕上显示视图。我想到了如何使用
carender
MTLTexture
实现这一点,但下面的方法存在一些问题

它在游乐场中运行,并将输出存储到下载中的
屏幕外渲染
文件夹:

导入应用工具包
进口金属
进口石英砂
导入PlaygroundSupport
let view=NSView(帧:CGRect(x:0,y:0,宽度:600,高度:400))
让圆=NSView(帧:CGRect(x:0,y:0,宽度:50,高度:50))
circle.wantsLayer=true
圆圈.layer?.backgroundColor=NSColor.red.cgColor
圆。层?角半径=25
view.wantsLayer=true
视图。添加子视图(圆形)
让textureDescriptor=MTLTextureDescriptor.texture2DDescriptor(像素格式:.rgba8Unorm,宽度:600,高度:400,mipmapped:false)
textureDescriptor.usage=[MTLTextureUsage.shaderRead、.shaderWrite、.renderTarget]
让设备=MTLCreateSystemDefaultDevice()!
让纹理:MTLTexture=device.makeTexture(描述符:textureDescriptor)!
let context=CIContext(mtlDevice:device)
let renderer=carender(mtlTexture:texture)
renderer.layer=view.layer
renderer.bounds=view.frame
让outputURL:URL=试试!FileManager.default.url(适用于:.downloadsDirectory,位于:.userDomainMask中,适用于:nil,create:false)
尝试FileManager.default.removietem(位于:outputURL)
尝试FileManager.default.createDirectory(at:outputURL,withmediateDirectory:true,attributes:nil)
变量帧号:Int=0
func render(){
Swift.print(“渲染帧(帧编号)…”)
renderer.beginFrame(atTime:caccurrentMediaTime(),时间戳:nil)
renderer.addUpdate(renderer.bounds)
render.render()
renderer.endFrame()
让ciImage:ciImage=ciImage(mtlTexture:texture)!
让cgImage:cgImage=context.createCGImage(ciImage,from:ciImage.extent)!
让url:url=outputURL.appendingPathComponent(“frame-\(frameNumber.png”)
let destination:cgmagedestination=cgmagedestinationcreatewithurl(url为CFURL,kuttypeppng,1,nil)!
CGImageDestinationAddImage(目标,cgImage,无)
guard CGImageDestinationFinalize(目标)else{fatalError()}
帧数+=1
}
变量计时器:计时器?
NSAnimationContext.runAnimationGroup({context在
context.duration=0.25
view.animator().frame.origin=CGPoint(x:550,y:350)
},completionHandler:{
计时器?.invalidate()
render()
Swift.print(“在\(outputURL.path)…..中完成\(frameNumber)帧的屏幕外呈现”)
})
//在动画开始和完成后立即进行第一次渲染。为了
//使用此演示计时器的一部分,而不是显示链接。
render()
timer=timer.scheduledTimer(withTimeInterval:1/30,repeats:true,block:{inrender()})
上述代码的问题如以下附件所示:

  • 纹理不会被清理,下一帧将在上一个渲染的顶部绘制。我知道我可以使用
    replace(region:…)
    ,但怀疑它与具有清晰颜色描述的渲染过程相比效率不高。这是真的吗?渲染过程可以与
    carender
    一起使用吗

  • 第一个帧(在实际项目中是两个三个帧)通常是空的。我怀疑这与
    carender
    渲染中或使用核心图像构建
    CGImage
    期间的某些异步行为有关。如何避免这种情况?在纹理上是否存在某种等待,直到渲染完成回调


  • 在与苹果开发者技术支持人员交谈后,似乎:

    Core Image延迟渲染,直到客户端请求访问帧缓冲区,即
    CVPixelBufferLockBaseAddress

    因此,解决方案只是在调用
    CIContext.render
    后执行
    CVPixelBufferLockBaseAddress
    ,如下所示:

    用于0中的帧编号。

    另外,这与问题的答案相同–您可能希望查看它,以了解在使用AVFoundation时,此问题可能发生在何处以及如何发生。尽管问题不同,但解决方案完全相同。

    我认为您可以使用
    AVVideoCompositionCoreAnimationTool
    来渲染带有动画的视图。

    Carender
    对于它对纹理的操作和纹理的状态非常不透明。对于问题1,我建议设置一个渲染过程描述符,使用加载操作来清除纹理,创建渲染命令编码器,立即结束编码器,并提交命令缓冲区。对于问题2,请尝试创建blit命令编码器,使用该编码器对纹理的同步资源命令进行编码,结束