У меня есть большой 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
следует использовать осторожно, потому что он вызывает вызовы ядра, которые могут повлиять на производительность, поэтому я предполагаю, что я просто скопирую соответствующие пиксели в новый буфер изображений, принимая во внимание грязные регионы. Имеет ли это смысл?
Вам, вероятно, не нужно беспокоиться о грязных регионах, но вместо этого следует основываться на ответе @ Tommy ... либо вызывать '-setNeedsDisplay' после того, как ваше содержимое изменилось, и позволить системе отображения вызвать вас для обновленного контента или синхронизировать ваш рисунок обновляется до обновления экрана с помощью 'CVDisplayLink' – nielsbot
(Мой комментарий применяется, за исключением случаев, когда ваш рисунок действительно очень большой). Вы можете прокомментировать свой код, чтобы найти узкое место. – nielsbot