iPad2上的CVOpenGLESTextureCacheCreateTextureFromImage太慢了,需要近30毫秒,太疯狂了。

4

我使用OpenGL ES在iPad上显示BGR24数据,但是我对OpenGL ES还不熟悉,所以在视频显示部分,我使用了苹果的RosyWriter示例代码。它可以运行,但是CVOpenGLESTextureCacheCreateTextureFromImage函数的执行时间超过了30毫秒,在RosyWriter中它的执行时间可以忽略不计。 我的做法是先将BGR24转换为BGRA像素格式,然后使用CVPixelBufferCreateWithBytes函数创建一个CVPixelBufferRef,最后通过CVOpenGLESTextureCacheCreateTextureFromImage获取CVOpenGLESTextureRef。下面是我的代码:

- (void)transformBGRToBGRA:(const UInt8 *)pict width:(int)width height:(int)height
{
rgb.data = (void *)pict;

vImage_Error error = vImageConvert_RGB888toARGB8888(&rgb,NULL,0,&argb,NO,kvImageNoFlags);
if (error != kvImageNoError) {
    NSLog(@"vImageConvert_RGB888toARGB8888 error");
}

const uint8_t permuteMap[4] = {1,2,3,0};

error = vImagePermuteChannels_ARGB8888(&argb,&bgra,permuteMap,kvImageNoFlags);
if (error != kvImageNoError) {
    NSLog(@"vImagePermuteChannels_ARGB8888 error");
}

free((void *)pict);
}

转换后生成CVPixelBufferRef,代码如下所示:
[self transformBGRToBGRA:pict width:width height:height];

CVPixelBufferRef pixelBuffer;
CVReturn err = CVPixelBufferCreateWithBytes(NULL,
                             width,
                             height,
                             kCVPixelFormatType_32BGRA, 
                             (void*)bgraData, 
                             bytesByRow, 
                             NULL, 
                             0,
                             NULL, 
                             &pixelBuffer);

if(!pixelBuffer || err)
{
    NSLog(@"CVPixelBufferCreateWithBytes failed (error: %d)", err);  
    return;
}

CVOpenGLESTextureRef texture = NULL;
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, 
                                                            videoTextureCache,
                                                            pixelBuffer,
                                                            NULL,
                                                            GL_TEXTURE_2D,
                                                            GL_RGBA,
                                                            width,
                                                            height,
                                                            GL_BGRA,
                                                            GL_UNSIGNED_BYTE,
                                                            0,
                                                            &texture);


if (!texture || err) {
    NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage failed (error: %d)", err);  
    CVPixelBufferRelease(pixelBuffer);
    return;
}

其他代码与RosyWriter示例几乎相似,包括着色器。因此我想知道为什么会出现这个问题,以及如何解决。

1
你要上传的图片尺寸是多少?你确定你没有在那30毫秒内测量-transformBGRToBGRA:方法的时间吗? - Brad Larson
是的,我确定。它是1024 * 768,transformBGRToBGRA的转换时间:我测量的是10毫秒。 - zhzhy
好的,那么您测量的30毫秒是从CVPixelBufferCreateWithBytes()调用之前到CVOpenGLESTextureCacheCreateTextureFromImage()调用之后的时间吗?这似乎非常高,因为我曾经看到iPad 2上传1080p帧(像素数增加了2.6倍)比30毫秒快得多。如果您只使用此数据和glTexImage2D(),那么您的时间是多少? - Brad Larson
实际上它非常高,事实上只有调用**CVOpenGLESTextureCacheCreateTextureFromImage()函数时才会花费30毫秒,而使用CVPixelBufferCreateWithBytes()函数则不需要任何时间。我只是用glTexImage2D()**重写了代码,它大约需要5毫秒,所以我认为这也很高,我花了一天以上的时间去找原因,并尝试解决这个问题,但没有答案。 - zhzhy
我似乎找到了答案,你能给我一些建议吗? - zhzhy
1个回答

5

通过我的研究,我发现当数据很大时(如3M),CVOpenGLESTextureCacheCreateTextureFromImage耗费大量时间的原因是分配、复制和移动操作,特别是复制操作。然后,使用像素缓冲池可以将CVOpenGLESTextureCacheCreateTextureFromImage的性能从30ms提升到5ms,与glTexImage2D()相同的水平。我的解决方案如下:

NSMutableDictionary*     attributes;
attributes = [NSMutableDictionary dictionary];


[attributes setObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
[attributes setObject:[NSNumber numberWithInt:videoWidth] forKey: (NSString*)kCVPixelBufferWidthKey];
[attributes setObject:[NSNumber numberWithInt:videoHeight] forKey: (NSString*)kCVPixelBufferHeightKey];

CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (CFDictionaryRef) attributes, &bufferPool);

CVPixelBufferPoolCreatePixelBuffer (NULL,bufferPool,&pixelBuffer);

CVPixelBufferLockBaseAddress(pixelBuffer,0);

UInt8 * baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer);

memcpy(baseAddress, bgraData, bytesByRow * videoHeight);

CVPixelBufferUnlockBaseAddress(pixelBuffer,0);

使用这个新创建的像素缓冲区,你可以让它更快速。
属性中添加以下配置可以使其性能最佳,小于1毫秒。
 NSDictionary *IOSurfaceProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                                                        [NSNumber numberWithBool:YES], @"IOSurfaceOpenGLESFBOCompatibility",[NSNumber numberWithBool:YES], @"IOSurfaceOpenGLESTextureCompatibility",nil];

[attributes setObject:IOSurfaceProperties forKey:(NSString*)kCVPixelBufferIOSurfacePropertiesKey];

1
我遵循了这个建议,添加了表面属性,并利用了像素缓冲池。但是,在iPad2上的性能并没有改善。现在我的代码中memcpy()函数占据了89%的时间。我不明白这种方法如何能改进性能,因为纹理上传是这段代码中时间关键的部分。如果像素数据已经存储在用户缓冲区中,再将内存复制到CVPixelBuffer中有什么好处呢? - MoDJ
这里也有同样的问题。如果您已经在内存中拥有BGRA数据,为什么不能将其包装在CVPixelBufferRef中? - jjxtra

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