将CIImage绘制到NSOpenGLView(无需点击主内存)

时间:2016-01-29 06:52:40

标签: objective-c swift cocoa gpu core-image

我觉得我必须在这里遗漏一些东西。我已经将NSOpenGLView子类化了,我试图在CIImage调用中绘制drawRect:

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倍(即通过更改{{1}声明到这个):

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 -> NSImage或支持NSImageView )。但这对我的情况来说不够高效。在我的应用程序中,我希望渲染几十个缩略图大小的图像,每个图像都有自己不同的CALayer链,并在底层基础图像发生变化时实时刷新它们。虽然它们每个都在几毫秒内呈现(使用我当前的CIFilters路径),但视图更新仍然是每秒几帧的数量级。

我目前使用的路径看起来像CGImage-bracketed。但似乎在渲染链的两端都有CGImageRef -> CIImage -> Bunch of CIFilters -> CGImage -> assign to CALayer for display会破坏性能。

在分析之后,似乎大部分时间用于复制内存,我认为这是预期的,但效率不高。支持CGImage需要穿梭到 GPU ,然后进行过滤,然后以CGImages的形式返回主内存,然后(可能)直接返回GPU进行缩放和显示由CGImage。理想情况下,根图像(在过滤之前)只会坐在GPU上,结果将直接呈现给视频内存,但我不知道如何实现这一点。我当前的渲染路径将像素粉碎在GPU上(这太棒了!),但是通过绕着内存来实际显示这个物品来淹没。

那么有没有人能够通过一条让GPU端到端保存内容的途径来进行核心图像过滤? (或者至少只需要交换一次数据?)如果我有一个IOSurface支持的CIImage,如何直接将其绘制到UI而不打击主内存?任何提示?

2 个答案:

答案 0 :(得分: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类没有返回正确的上下文。谁知道。仍然对原因感兴趣,但至少我现在可以继续前进。

答案 1 :(得分: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)")
}