Swift 如何从背景线程向CametLayer渲染CIImage

Swift 如何从背景线程向CametLayer渲染CIImage,swift,macos,core-image,cametallayer,Swift,Macos,Core Image,Cametallayer,应该可以从背景线程渲染到CametLayer,如下所示。注意,我已经注释掉了DispatchQueue.global.async{},因为它生成了一个SwiftUI错误,因为更新必须来自主线程 如果没有,那么什么是正确/最好的方法来避免阻塞UI线程?如果可能的话。我想在用户拖动调整滑块时进行渲染,但如果图像大小太大,UI会变得不稳定,这似乎会影响性能 不知何故,Pixelmator Pro似乎实现了一种方法,允许对图像进行调整,而不会出现明显的UI延迟或结巴。如有任何建议,将不胜感激 func

应该可以从背景线程渲染到CametLayer,如下所示。注意,我已经注释掉了
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
        }
    }
}