(更新以下...... )
OpenGL Cocoa / OSX Desktop ap,目标是10.8 +
我有一种情况,我每帧收到一次NSImage对象,并希望将其转换为openGL纹理(它是来自IPCamera的视频帧)。从SO和互联网,我可以找到将NSImage转换为glTexture的最有用的实用方法是下面的代码。
虽然这对于偶尔(IE加载)情况来说很好,但它是一个巨大的性能值,并且每帧运行一次很糟糕。我已经分析了代码并将瓶颈缩小到两个调用。这些呼叫一起占我整个应用程序运行时间的近三分之二。
bitmaprep创建
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];
glTexImage2D调用(我不知道如何改进它。)
如何使下面的方法更有效?
或者,告诉我,我做错了,我应该去别处看看?这些帧是作为MJPEG进入的,所以我可能会在它转换为NSImage之前使用NSData。然而,它的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];
}
根据Brad的评论,我决定简单地绕过NSImage。在我的特定情况下,我能够在将JPG数据转换为NSImage之前将其作为NSData对象进行访问,因此这很有用:
[NSBitmapImageRep imageRepWithData: imgData];
使用或多或少相同的方法,但直接从位图代表开始,CPU使用率从80%下降到20%,我对速度感到满意。我有一个解决方案,我的ap。
我仍然想知道我原来的问题是否有答案,或者是否最好接受这个作为避免目标的对象课程。最后,我仍然想知道是否有可能改善glTexImage2D呼叫的加载时间 - 虽然它现在已经在合理的范围内,但它仍然可以占据99%的负载该方法(但也许没关系。)
===========
答案 0 :(得分:2)
根据您的数据进入方式,您可以使用像素缓冲区对象(PBO),它在上传纹理时使用DMA(避开CPU),而随机指针将使用memcpy复制(使用CPU) )。
Apple在此处描述了如何执行此操作:https://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/opengl-macprogguide/opengl_texturedata/opengl_texturedata.html
基本上,PBO为您提供了一块OpenGL可以DMA的内存,因此使用它来分配您复制解压缩帧的内存,然后通过绑定缓冲区并使用nil作为内存引用调用glTexSubImage2D来绘制。
这确实依赖于您能够将收到的数据直接输入公益组织 - 否则您仍然需要向公益组织发送电子邮件,从而失去它带来的任何好处。