0

У меня есть большой NSView, который показывает пользовательский чертеж Quartz2D, который многократно меняется с высокой частотой кадров. Однако некоторые части чертежа могут меняться от кадра к кадру. Мой подход до сих пор вначале рисуется в контекстном растровом контексте, а затем создает изображение из этого контекста и, наконец, обновляет содержимое слоя CoreAnimation на этом изображении.Как отобразить многократно нарисованный растровый контекст CoreGraphics на экране без мерцания с использованием Core Animation?

Мой первый вопрос: насколько этот подход имеет смысл, и если это способ пойти, когда дело доходит до производительности?

Рисование в контекстном растровом контексте работает достаточно быстро и оптимизировано для перерисовки только грязных областей. Итак, после этого шага у меня есть набор прямоугольников, которые отмечают области в буфере вне экрана, которые должны отображаться на экране. На данный момент я просто обновляю содержимое слоя CoreAnimation с помощью изображения, созданного из контекстного растрового контекста, который в основном работает, но я становлюсь мерцающим. Похоже, что новые кадры вскоре отображаются на экране, пока они не полностью (или нет в все). Я играл с CATransaction lock/unlock/begin/end/flush, NSView lockFocus/unlockFocus, NSDisableScreenUpdates/NSEnableScreenUpdates, но еще не нашел способ обойти мерцание, поэтому мне было интересно, какая на самом деле правильная последовательность для правильной синхронизации?

Вот набросок кода инициализации:

NSView* theView = ... 

CALayer* layer = [[CALayer new] autorelease]; 
layer.actions = [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"contents"]; 
[theView setLayer: layer]; 
[theView setWantsLayer: YES]; 

// bitmapContext gets re-created when the view size increases. 
CGContextRef bitmapContext = CGBitmapContextCreate(...); 

А вот эскиз кода рисования:

CGRect[] dirtyRegions = ... 

NSDisableScreenUpdates(); 

[CATransaction begin]; 
[CATransaction setDisableActions: YES]; 

// draw into dirty regions of bitmapContext 
// ... 

// create image from bitmap context 
void* buffer = CGBitmapContextGetData(bitmapContext); 
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, ...); 
CGImageRef image = CGImageCreate(..., provider, ...); 

// update layer contents, dirty regions are ignored 
layer.contents = image; 

[CATransaction commit]; 

NSEnableScreenUpdates(); 

Я также хотел бы воспользоваться знанием о грязных регионах , Есть ли способ обновить только грязные области на экране, используя этот подход?

Спасибо за помощь!

ОБНОВЛЕНИЕ: Думаю, я нашел проблему, которая вызывает мерцание. Я создаю изображение с буфером пикселей из контекста растрового изображения, используя CGImageCreate(...). Если я использую CGBitmapContextCreateImage(...), он работает. CGBitmapContextCreateImage копирует на запись, поэтому записывает пиксели, когда контекст растрового изображения обновляется снова, если я правильно понимаю, что объясняет, почему он не работал раньше. Я где-то читал, что CGBitmapContextCreateImage следует использовать осторожно, потому что он вызывает вызовы ядра, которые могут повлиять на производительность, поэтому я предполагаю, что я просто скопирую соответствующие пиксели в новый буфер изображений, принимая во внимание грязные регионы. Имеет ли это смысл?

+0

Вам, вероятно, не нужно беспокоиться о грязных регионах, но вместо этого следует основываться на ответе @ Tommy ... либо вызывать '-setNeedsDisplay' после того, как ваше содержимое изменилось, и позволить системе отображения вызвать вас для обновленного контента или синхронизировать ваш рисунок обновляется до обновления экрана с помощью 'CVDisplayLink' – nielsbot

+0

(Мой комментарий применяется, за исключением случаев, когда ваш рисунок действительно очень большой). Вы можете прокомментировать свой код, чтобы найти узкое место. – nielsbot

ответ

1

После опроса множества различных подходов я отказался от использования CoreAnimation для загрузки данных пикселей и решил использовать пиксельные буферы CoreVideo (CVPixelBufferRef) в сочетании с OpenGL для перемещения пикселей на экране вместо этого.

CoreVideo предоставляет некоторые удобные функции для создания OpenGL текстуры из пиксельных буферов (CVOpenGLTextureCacheCreateTextureFromImage), управлять ими в кэш текстур (CVOpenGLTextureCacheRef), и рисовать в буфер безопасно (CVPixelBufferLockBaseAddress/CVPixelBufferUnlockBaseAddress). Загрузка грязных прямоугольников в оконный буфер обратно может быть выполнена с помощью обычных команд сопоставления текстур OpenGL (glTexCoord2fv).

Другой подход, который работает одинаково хорошо и имеет аналогичный API, - IOSurface, более подробная информация об этом - here.

1

«Обычный» способ заключается в том, чтобы работать наоборот: позвоните по номеру CALayer -setNeedsDisplay, чтобы указать, когда доступно изменение содержимого, и ответьте на -drawInContext:, чтобы сделать запрос по требованию. Таким образом, вы позволяете извлекать содержимое слоя из вас, вы не нажимаете на них.

Я должен признать, что я очень удивлен, что вы рвутесь, пытаясь протолкнуть, но предположим, что вы начали с простейшей всего лишь layer.contents = image, и вся дополнительная сложность, которую вы добавили с транзакциями, и блокировка обновления экрана пытается обойти разрыв, и вы абсолютно уверены, что не создаете проблем, злоупотребляя своим кодом, что вы, вероятно, должны сделать, это очередь ваших обновлений, создать CVDisplayLink и затем нажимать любые ожидающие обновления только тогда, когда соответствующий дисплей или дисплеи будет обновляться. Это в основном тот же подход, что и обновление только во время вертикального возврата на старом выходе на основе CRT.

+0

Большое спасибо за ваш ответ! Я пробовал и наоборот, это не имеет никакого отношения к разрыву. Причина, по которой я ушел от подхода setNeedsDisplay, заключается в том, что, делая первые шаги с CoreAnimation i, я не знал, что мне нужно повторно создать CGImageRef и обновлять содержимое слоя каждый раз, когда изменяется контекст исходного растрового изображения, поскольку оба они созданы с тем же буфером данных. Также я не был уверен, что это уберет контроль над частотой кадров ... CVDisplayLink звучит интересно, заглянет в него, спасибо! – vosc