在不影响主内存的情况下将CIImage绘制到NSOpenGLView上

3

我觉得我可能漏掉了什么。我已经创建了 NSOpenGLView 的子类,并尝试在 drawRect: 方法中绘制一个 CIImage

override func drawRect(dirtyRect: NSRect) {
    super.drawRect(dirtyRect)
    let start = CFAbsoluteTimeGetCurrent()

    openGLContext!.makeCurrentContext()
    let cglContext = openGLContext!.CGLContextObj
    let pixelFormat = openGLContext!.pixelFormat.CGLPixelFormatObj
    let colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB)!
    let options: [String: AnyObject] = [kCIContextOutputColorSpace: colorSpace]
    let context = CIContext(CGLContext: cglContext, pixelFormat: pixelFormat, colorSpace: colorSpace, options: options)

    context.drawImage(inputImage, inRect: self.bounds, fromRect: input.extent)

    openGLContext!.flushBuffer()
    let end = CFAbsoluteTimeGetCurrent()
    Swift.print("updated view in \(end - start)")
}

我显然错误地认为NSOpenGLContext(及其底层的CGLContext)可以包装在CIContext中,并且通过这种方式进行渲染可以在视图中生成图像。但是,尽管上述代码正在执行,我不知道实际像素最终会出现在哪里(因为视图最终为空白)。
如果我只是获取当前的NSGraphicsContext并将其渲染到其中,那么我就可以在NSOpenGLView中得到一张图片,但是渲染似乎需要花费约10倍的时间(即通过将CIContext声明更改为以下内容):
    // Works, but is slow
    let context = NSGraphicsContext.currentContext()!.CIContext!

我也尝试过这个方法,它既慢而且没有实际显示图片,所以是双重失败。
    // Doesn't work. AND it's slow.
    input.drawInRect(bounds, fromRect: input.extent, operation: .CompositeDestinationAtop, fraction: 1.0)

简单的解决方案是将其渲染为CGImage,然后通过CGImage -> NSImageNSImageView或支持CALayer的方式将其弹出到屏幕上。但对于我的情况来说,这并不够高效。在我的应用程序中,我想要渲染几十个缩略图大小的图像,每个图像都有自己不同的CIFilters链,并在其基础图像更改时实时刷新它们。虽然它们每个都需要几毫秒的时间进行渲染(使用当前的CGImage-bracketed路径),但视图更新仍然每秒只有几帧。
我目前使用的路径类似于CGImageRef -> CIImage -> 一堆CIFilters -> CGImage -> 分配给CALayer显示。但似乎在渲染链的两端都使用CGImages会影响性能。
经过分析,大部分时间都花费在复制内存上,这可能是预期的,但并不高效。支持CALayer的后备CGImage需要被传输到GPU,然后被过滤,然后作为CGImage返回到主内存,然后(可能)立即回到GPU以被缩放并由CALayer显示。理想情况下,在过滤之前,根图像将只停留在GPU上,并且结果将直接呈现到视频内存中,但我不知道如何实现这一点。我的当前渲染路径在GPU上进行了像素处理(这很棒!),但是在传输内存以实际显示它时被淹没了。
那么,有人可以告诉我如何使用端到端的路径进行Core Image过滤吗?(或者至少只需要交换数据一次?)如果我有一个基于IOSurface的CIImage,我如何直接将其绘制到UI而不会影响主内存?有任何提示吗?
2个回答

0
经过一整天的在这个问题上研究后,使用 NSOpenGLView 仍然没有结果... 但是,我认为我可以通过使用 CAOpenGLLayer 来实现我想要的效果,这对我来说完全没问题。
class GLLayer: CAOpenGLLayer {
    var image: CIImage?

    override func drawInCGLContext(ctx: CGLContextObj, pixelFormat pf: CGLPixelFormatObj, forLayerTime t: CFTimeInterval, displayTime ts: UnsafePointer<CVTimeStamp>) {
        if image == nil {
            let url = NSURL(fileURLWithPath: "/Users/doug/Desktop/test.jpg")
            image = CIImage(contentsOfURL: url)!
        }

        let colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB)!
        let options: [String: AnyObject] = [kCIContextOutputColorSpace: colorSpace]
        let context = CIContext(CGLContext: ctx, pixelFormat: pf, colorSpace: colorSpace, options: options)
        let targetRect = CGRectMake(-1, -1, 2, 2)
        context.drawImage(image!, inRect: targetRect, fromRect: image!.extent)
    }
}

需要注意的一件事是,CAOpenGLView 的坐标系不同。视图大小为2.0x2.0单位,原点在0,0处(花了一段时间才弄清楚)。除此之外,这基本上与我原来问题中不起作用的代码相同(只是在这里它实际上是有效的)。也许NSOpenGLView类没有返回正确的上下文。谁知道呢。仍然对为什么感兴趣,但至少现在我可以继续了。


0

代码应该能够与以下添加/修改一起工作:

var ciContext: CIContext!

override func drawRect(dirtyRect: NSRect) {
    super.drawRect(dirtyRect)
    let start = CFAbsoluteTimeGetCurrent()

    openGLContext!.makeCurrentContext()
    let cglContext = openGLContext!.CGLContextObj
    let attr = [
        NSOpenGLPixelFormatAttribute(NSOpenGLPFAAccelerated),
        NSOpenGLPixelFormatAttribute(NSOpenGLPFANoRecovery),
        NSOpenGLPixelFormatAttribute(NSOpenGLPFAColorSize), 32,
        0
    ]
    let pixelFormat = NSOpenGLPixelFormat(attributes: attr)
    let colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB)!
    let options: [String: AnyObject] = [kCIContextOutputColorSpace:colorSpace, kCIContextWorkingColorSpace:colorSpace]
    ciContext = CIContext(CGLContext: cglContext, pixelFormat: pixelFormat!.CGLPixelFormatObj, colorSpace: colorSpace, options: options)
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
    // setup viewing projection
    glMatrixMode(GLenum(GL_PROJECTION))
    // start with identity matrix
    glLoadIdentity();

    // Create an image
    let image = NSImage(contentsOfFile: "/Users/kuestere/Pictures/DSCI0004.JPG")
    let imageData = image?.TIFFRepresentation!
    let ciImage = CIImage(data: imageData!)
    if (ciImage == nil) {
        Swift.print("ciImage loading error")
    }
    let imageFrame = ciImage!.extent
    let viewFrame = self.visibleRect
    // setup a viewing world left, right, bottom, top, zNear, zFar
    glOrtho(0.0, Double(viewFrame.width), 0.0, Double(viewFrame.height), -1.0, 1.0);
    ciContext.drawImage(ciImage!, inRect: self.visibleRect, fromRect: imageFrame)
    glFlush();
    let end = CFAbsoluteTimeGetCurrent()
    Swift.print("updated view in \(end - start)")
}

1
最好解释一下对原始代码所做的任何更改以及为什么进行更改,这将非常有帮助。 - miken32

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接