【问题标题】:Drawing a CIImage to a NSOpenGLView (without hitting main memory)将 CIImage 绘制到 NSOpenGLView(不影响主内存)
【发布时间】:2016-01-29 06:52:39
【问题描述】:

我觉得我必须在这里遗漏一些东西。我已经对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 中,并且渲染到该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 -> NSImage,然后是NSImageView,或支持CALayer)。但这对我的情况来说还不够高效。在我的应用程序中,我希望渲染几十个缩略图大小的图像,每个图像都有自己不同的CIFilters 链,并随着基础图像的变化实时刷新它们。虽然它们每个都在几毫秒内渲染(使用我当前的 CGImage-bracketed 路径),但视图更新仍然是每秒几帧的数量级。

我目前正在使用类似于CGImageRef -> CIImage -> Bunch of CIFilters -> CGImage -> assign to CALayer for display 的路径。但似乎在渲染链的两端都有CGImages 会影响性能。

分析后,似乎大部分时间都花在了复制内存上,我认为这是预期的,但效率不高。支持的 CGImage 需要穿梭到 GPU,然后过滤,然后以 CGImage 的形式返回主内存,然后(可能)直接返回 GPU 以由CALayer。理想情况下,根图像(过滤之前)将只位于 GPU 上,结果将直接呈现到视频内存,但我不知道如何实现这一点。我当前的渲染路径在 GPU 上完成了像素粉碎(这太棒了!),但是被到处穿梭的内存淹没以实际显示该死的东西。

那么,任何人都可以告诉我如何使用将事物端到端保持在 GPU 上的路径进行核心图像过滤吗? (或者至少只需要交换一次数据?)如果我有一个支持 IOSurface 的 CIImage,我如何在不影响主内存的情况下直接将其绘制到 UI 上?有什么提示吗?

【问题讨论】:

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


    【解决方案1】:

    经过一天的头撞墙后,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(花了一些时间才弄清楚)。除此之外,这与我原来的问题中的非工作代码基本相同(除了,你知道,它实际上 works 在这里)。也许NSOpenGLView 类没有返回正确的上下文。谁知道。仍然对为什么感兴趣,但至少我现在可以继续前进。

    【讨论】:

      【解决方案2】:

      代码应与以下添加/修改一起使用:

      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)")
      }
      

      【讨论】:

      • 解释对原始代码所做的任何更改以及进行更改的原因会很有帮助。
      猜你喜欢
      • 2018-08-09
      • 1970-01-01
      • 1970-01-01
      • 2012-02-16
      • 1970-01-01
      • 1970-01-01
      • 2014-04-03
      • 2012-09-01
      • 1970-01-01
      相关资源
      最近更新 更多