问题描述
是否可以从后台线程渲染到 cametallayer,如下所示。请注意,我已注释掉 dispatchQueue.global.async{}
,因为它会生成 SwiftUI 错误,因为更新必须来自主线程。
如果不是,那么避免阻塞 UI 线程的正确/最佳方法是什么 - 如果这可能的话。我想在用户拖动调整滑块时进行渲染,但如果图像尺寸太大,UI 似乎会变得不稳定,从而影响性能。
不知何故,Pixelmator Pro 似乎实现了一种方法,可以将调整应用于图像,而不会出现明显的 UI 延迟或卡顿。任何建议将不胜感激。
func display(_ layer: CALayer) {
// dispatchQueue.global(qos: .userInitiated).async {
if let drawable = self.MetalLayer.nextDrawable(),let commandBuffer = self.commandQueue.makeCommandBuffer() {
let colorAttachment = self.passDescriptor.colorAttachments[0]!
colorAttachment.texture = drawable.texture
colorAttachment.loadAction = .clear
colorAttachment.storeAction = .dontCare
colorAttachment.clearColor = MTLClearColor(red: 0,green: 0,blue: 0,alpha: 0)
if let rawFilter = self.rawFilter {
// required in order to clear the screen if no selection
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: self.passDescriptor)!
renderEncoder.endEncoding()
if let processed = self.process(rawFilter) {
let x = self.size.width/2 - processed.extent.width/2
let y = self.size.height/2 - processed.extent.height/2
self.context.render(processed,to: drawable.texture,commandBuffer: commandBuffer,bounds: CGRect(x:-x,y:-y,width: self.size.width,height: self.size.height),colorSpace: self.colorSpace)
}
}
else {
// required in order to clear the screen if no selection
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: self.passDescriptor)!
renderEncoder.endEncoding()
}
commandBuffer.commit()
commandBuffer.waitUntilScheduled()
drawable.present()
// }
}
}
解决方法
好的,我刚刚弄清楚为什么我的解决方案不起作用 - 我在 process() 函数中创建了一个图像直方图并设置了一个 UI 控件图像 - 这只是需要包装在一个 DispatchQueue.main.async{} 调用中.
为了防止响应滑块移动而调用过多,除非渲染周期已完成,否则不要调用它
var isBusy = false
func display(_ layer: CALayer) {
guard !self.isBusy else {
return
}
self.isBusy = true
DispatchQueue.global(qos: .userInitiated).async {
if let drawable = self.metalLayer.nextDrawable(),let commandBuffer = self.commandQueue.makeCommandBuffer() {
let colorAttachment = self.passDescriptor.colorAttachments[0]!
colorAttachment.texture = drawable.texture
colorAttachment.loadAction = .clear
colorAttachment.storeAction = .dontCare
colorAttachment.clearColor = MTLClearColor(red: 0,green: 0,blue: 0,alpha: 0)
if let rawFilter = self.rawFilter {
// Required in order to clear the screen if no selection
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: self.passDescriptor)!
renderEncoder.endEncoding()
if let processed = self.process(rawFilter) {
let x = self.size.width/2 - processed.extent.width/2
let y = self.size.height/2 - processed.extent.height/2
self.context.render(processed,to: drawable.texture,commandBuffer: commandBuffer,bounds: CGRect(x:-x,y:-y,width: self.size.width,height: self.size.height),colorSpace: self.colorSpace)
}
}
else {
// Required in order to clear the screen if no selection
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: self.passDescriptor)!
renderEncoder.endEncoding()
}
commandBuffer.commit()
commandBuffer.waitUntilScheduled()
// Present on the main thread - not sure if this is necessary but it seems like it to get the UI to update
DispatchQueue.main.async {
drawable.present()
}
self.isBusy = false
}
}
}