(ОБНОВЛЕНИЕ НИЖЕ...)
Приложение OpenGL Cocoa/OSX Desktop для версии 10.8+
У меня есть ситуация, когда я получаю объект NSImage один раз за кадр и хотел бы преобразовать его в текстуру openGL (это видеокадр с IP-камеры). Из SO и Интернета наиболее полезным служебным методом, который я смог найти для преобразования NSImage в glTexture, является приведенный ниже код.
Хотя это нормально для случайных ситуаций (загрузка IE), это огромная нагрузка на производительность, и ее ужасно запускать один раз за кадр. Я профилировал код и сократил узкое место до двух вызовов. Вместе эти вызовы составляют почти две трети времени работы всего моего приложения.
Создание растрового изображения
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, поэтому я потенциально мог бы использовать 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 в виде объекта NSData до того, как он был преобразован в NSImage, и это отлично сработало:
[NSBitmapImageRep imageRepWithData: imgData];
Используя более или менее тот же метод, описанный выше, но начиная непосредственно с изображения растрового изображения, загрузка ЦП упала с 80% до 20%, и я доволен скоростью. У меня есть решение для моего ап.
Я все еще хотел бы знать, есть ли ответ на мой первоначальный вопрос или лучше просто принять это как наглядный урок того, чего следует избегать. Наконец, мне все еще интересно, возможно ли улучшить время загрузки при вызове glTexImage2D - хотя теперь оно находится в пределах разумного, оно по-прежнему профилируется как занимающее 99% загрузки метода (но, может быть, это нормально.)
===========
glTexImage2D()
действительно самая медленная часть из вышеперечисленных? Размещение этого в NSImage и перерисовка через Core Graphics, казалось бы, намного медленнее, чем реальная загрузка текстуры. - person Brad Larson   schedule 31.03.2014