2

下面更新...

面向 10.8+ 的 OpenGL Cocoa/OSX 桌面应用程序

我有一种情况,我每帧接收一次 NSImage 对象,并希望将其转换为 openGL 纹理(它是来自 IPCamera 的视频帧)。从 SO 和互联网上,我可以找到将 NSImage 转换为 glTexture 的最有用的实用方法是下面的代码。

虽然这对于偶尔的(IE 加载)情况来说很好,但它是一个巨大的性能消耗,并且每帧运行一次很糟糕。我已经分析了代码并将瓶颈缩小到两个调用。这些调用加起来占整个应用程序运行时间的近三分之二。

  1. 位图制作

     NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithData:[inputImage TIFFRepresentation]];
    

    认为问题可能出在我获取位图的方式上,并且因为我听说过关于 TIFFRepresentation 的性能不好,所以我尝试了这个,它确实更快,但只有一点点并且还引入了一些奇怪的颜色偏移(一切看起来都是红色的):

    NSRect rect = NSMakeRect(0, 0, inputImage.size.width, inputImage.size.height);
    CGImageRef cgImage = [inputImage CGImageForProposedRect:&rect context:[NSGraphicsContext currentContext] hints:nil];
    NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
    
  2. glTexImage2D 调用(我不知道如何改进它。)

我怎样才能使下面的方法更有效?

或者,告诉我我做错了,我应该去别处看看?帧以 MJPEG 格式输入,因此我可以在将 NSData 转换为 NSImage 之前使用它。但是,它是 jpeg 编码的,所以我必须处理它。我实际上还希望 NSImage 对象用于应用程序的另一部分。

-(void)loadNSImage:(NSImage*)inputImage intoTexture:(GLuint)glTexture{


    // If we are passed an empty image, just quit
    if (inputImage == nil){
        //NSLog(@"LOADTEXTUREFROMNSIMAGE: Error: you called me with an empty image!");
        return;
    }


    // We need to save and restore the pixel state
    [self GLpushPixelState];
    glEnable(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, glTexture);


    //  Aquire and flip the data
    NSSize imageSize = inputImage.size;
    if (![inputImage isFlipped]) {
        NSImage *drawImage = [[NSImage alloc] initWithSize:imageSize];
        NSAffineTransform *transform = [NSAffineTransform transform];

        [drawImage lockFocus];

        [transform translateXBy:0 yBy:imageSize.height];
        [transform scaleXBy:1 yBy:-1];
        [transform concat];

        [inputImage drawAtPoint:NSZeroPoint
                                fromRect:(NSRect){NSZeroPoint, imageSize}
                               operation:NSCompositeCopy
                                fraction:1];

        [drawImage unlockFocus];

        inputImage = drawImage;
    }

    NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithData:[inputImage TIFFRepresentation]];


    //  Now make a texture out of the bitmap data
    // Set proper unpacking row length for bitmap.
    glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)[bitmap pixelsWide]);

    // Set byte aligned unpacking (needed for 3 byte per pixel bitmaps).
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    NSInteger samplesPerPixel = [bitmap samplesPerPixel];

    // Nonplanar, RGB 24 bit bitmap, or RGBA 32 bit bitmap.
    if(![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)) {

        // Create one OpenGL texture
        // FIXME: Very slow
        glTexImage2D(GL_TEXTURE_2D, 0,
                     GL_RGBA,//samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8,
                     (GLint)[bitmap pixelsWide],
                     (GLint)[bitmap pixelsHigh],
                     0,
                     GL_RGBA,//samplesPerPixel == 4 ? GL_RGBA : GL_RGB,
                     GL_UNSIGNED_BYTE,
                     [bitmap bitmapData]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    }else{
        [[NSException exceptionWithName:@"ImageFormat" reason:@"Unsupported image format" userInfo:nil] raise];
    }
    [self GLpopPixelState];
}

简介截图:

在此处输入图像描述

更新

根据下面布拉德的评论,我决定简单地绕过 NSImage。在我的特殊情况下,我能够在将 JPG 数据转换为 NSImage 之前将其作为 NSData 对象访问,因此效果很好:

  [NSBitmapImageRep imageRepWithData: imgData];

使用或多或少与上述相同的方法,但直接从位图表示开始,CPU 使用率从 80% 下降到 20%,我对速度感到满意。我有我的应用程序的解决方案。

我仍然想知道我最初的问题是否有答案,或者最好只是接受这个作为避免什么的对象教训。最后,我仍然想知道是否有可能改善 glTexImage2D 调用的加载时间——尽管它现在已经在合理的范围内,但它仍然占用了该方法 99% 的负载(但也许没关系。)

===========

4

1 回答 1

2

根据数据的输入方式,您可以使用像素缓冲区对象 (PBO),它将在上传纹理时使用 DMA(避免 CPU),而随机指针将使用 memcpy(使用 CPU)复制。

苹果在这里描述了如何做到这一点:https ://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/opengl-macprogguide/opengl_texturedata/opengl_texturedata.html

基本上,PBO 为您提供了一块 OpenGL 可以 DMA 的内存,因此使用它来分配您将解压缩帧复制到其中的内存,然后通过绑定缓冲区并使用 nil 作为内存参考调用 glTexSubImage2D 来绘制。

这确实依赖于您能够将传入的数据直接带入 PBO - 否则您仍然需要 memcpy 到 PBO,从而失去它可能带来的任何好处。

于 2014-04-01T07:15:48.330 回答