#objective-c #macos #cocoa
#objective-c #macos #какао
Вопрос:
Какой самый быстрый способ рендеринга и масштабирования изображения на поверхности с высокой частотой кадров?
Я рисую (с масштабированием) NSBitmapImageRep, используя drawRect со скоростью ~ 30 кадров в секунду, но он использует огромное количество процессора.
Пример кода для установки пикселей в NSBitmapImageRep со скоростью 30 кадров в секунду:
NSInteger x, y;
unsigned char *imgptr = nsBitmapImageRepObj.bitmapData;
unsigned char *ptr;
NSInteger rowBytes = nsBitmapImageRepObj.bytesPerRow;
for (y = 0; y < nsInputFrameRect.size.height; y ) {
ptr = imgptr (y * rowBytes);
for (x = 0; x < nsInputFrameRect.size.width; x ) {
*ptr = 1; // R
*ptr = 2; // G
*ptr = 3; // B
}
}
[self setNeedsDisplay:YES];
Отрисовка происходит со скоростью 30 кадров в секунду:
- (void)drawRect:(NSRect)pRect {
[NSGraphicsContext saveGraphicsState];
[nsBitmapImageRepObj drawInRect:pRect];
[NSGraphicsContext restoreGraphicsState];
} // end drawRect
drawInRect в NSBitmapImageRep использует много процессора. Какой самый быстрый способ масштабирования и рисования изображения с высокой частотой кадров? Исходным изображением должно быть изображение, для которого я могу установить пиксели напрямую, например, путем получения указателя BitmapData.
Если я преобразовываю NSBitmapImageRep в CIImage и рисую с помощью [CIImage drawInRect], то это примерно на 10% быстрее. Если я буду рисовать в NSOpenGLView вместо NSView, это снова будет примерно на 10% быстрее, но все равно scale / drawInRect занимает много процессорного времени.
Комментарии:
1. Вы пытаетесь раскрасить каждый пиксель на основе формулы? Этот вид операций лучше всего подходит для графического процессора. В зависимости от того, что именно вы собираетесь делать, ваше решение может варьироваться от шейдеров OpenGL до CoreImage и даже просто Quartz.
2. Это для простой графики караоке, но это не шаблонно: я обновляю изображение NSBitmapImageRep с низким разрешением, а затем масштабирую это маленькое изображение до размера экрана со скоростью около 30 кадров в секунду. Установка пикселей в маленьком NSBitmapImageRep выполняется очень быстро, но регулярное масштабирование изображения до размера экрана с помощью drawInRect выполняется очень медленно. Быстрее ли масштабировать изображение? Или есть какой-то более быстрый способ масштабирования изображений, когда исходные изображения могут быть установлены попиксельно?
Ответ №1:
Прежде всего, вы должны удалить сохранение и восстановление NSGraphicsContext
, потому -[NSImageRep drawInRect:]
что метод делает это сам . См. «Контекст рисования графики Cocoa»:
Important: Saving and restoring the current graphics state is a relatively expensive operation that should done as little as possible.
Следующий момент — это структура ваших пиксельных данных: 8 бит на выборку и 3 выборки на пиксель, что означает 24 бита на пиксель. Это очень неудобно для оборудования (GPU?), Потому что 32-битные значения обрабатываются быстрее, чем 24-битные значения. Есть простой способ улучшить (ускорить) обработку пикселей: используйте 4 байта вместо 3: добавьте байт заполнения к каждому пикселю, что не повредит изображению! Этот байт заполнения не является частью изображения, а только частью хранилища, в котором хранится пиксель, и не используется ни в одной операции рисования. Это просто здесь, чтобы ускорить операции с пикселями. Поэтому используйте это в описании вашего NSBitImageRep
:
bitsPerSample:8
samplesPerPixel:3
hasAlpha:NO
isPlanar:NO // must be meshed
bytesPerRow:0 // let the system do it
bitsPerPixel:32 // <-- important !!
И поэтому ваш код должен быть изменен:
*ptr = 3; // B
*ptr = 123; // new!! value is unimportant
Кстати: если вы читаете jpeg-изображение в NSBitmapImageRep
, то эти изображения всегда содержат 3-байтовые пиксели плюс байт заполнения. Зачем такая трата памяти? Для этого есть веские причины!
Комментарии:
1. Спасибо, я удалил сохранение контекста и перешел на 32 бит / с. Это имеет большой смысл, но 32 бит / с не оказали заметного влияния на производительность в моем случае. Для увеличения с 200×200 до 400×400 требуется около 7% ЦП, но для увеличения примерно до 1600×1600 требуется 34% ЦП, что, по-видимому, все в вызове drawInRect(). Возможно, NSBitmapImageRep drawInRect не использует графический процессор для меня. Является ли более целесообразным использовать CIImage, чтобы убедиться, что он обрабатывается графическим процессором?