Ведущие циклы рендеринга OpenGL
Q: Как я управляю циклом получения своего Какао приложение OpenGL?
A: На Mac OS X 10.4 и позже, приложения должны использовать Базовую ссылку Видеодисплея (CVDisplayLink) управлять циклом получения Какао приложение OpenGL. Для совместимости с Mac OS X 10.3 и ранее, они должны использовать таймер Какао (NSTimer).
Приложения OpenGL должны избежать кадра рвущиеся и пропадающие впустую ресурсы, получающие пиксели, которые никогда не будет видеть пользователь. Чтобы сделать это, приложения не должны рисовать быстрее, чем дисплей может обновить и должен подкачать или нарисовать во время вертикального интервала обратного хода луча. CVDisplayLink обеспечивает отдельный высокоприоритетный поток для уведомления приложения, когда данному дисплею будет нужен каждый кадр, удостоверяясь, что обновление не наложится с ранее представленным кадром. То, как часто кадр требуют, основывается на частоте обновления дисплея, в настоящее время связываемого со ссылкой дисплея. Перечисление 1 показывает пример того, как использовать CVDisplayLink управлять Вашим циклом получения. Для получения дополнительной информации о CVDisplayLink функции, посмотрите
Выборка перечисления 1 Базовая ссылка Видеодисплея
@interface MyView : NSOpenGLView |
{ |
CVDisplayLinkRef displayLink; //display link for managing rendering thread |
} |
@end |
- (void)prepareOpenGL |
{ |
// Synchronize buffer swaps with vertical refresh rate |
GLint swapInt = 1; |
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; |
// Create a display link capable of being used with all active displays |
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); |
// Set the renderer output callback function |
CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self); |
// Set the display link for the current renderer |
CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; |
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; |
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); |
// Activate the display link |
CVDisplayLinkStart(displayLink); |
} |
// This is the renderer output callback function |
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) |
{ |
CVReturn result = [(MyView*)displayLinkContext getFrameForTime:outputTime]; |
return result; |
} |
- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime |
{ |
// Add your drawing codes here |
return kCVReturnSuccess; |
} |
- (void)dealloc |
{ |
// Release the display link |
CVDisplayLinkRelease(displayLink); |
[super dealloc]; |
} |
При использовании таймера Какао (NSTimer) на Mac OS X 10.3 и ранее, существует несколько вещей иметь в виду. Обязательно включить вертикальную синхронизацию, чтобы предотвратить разрыв кадра и использовать интервал подходящего времени. Перечисление 2 показывает пример того, как использовать NSTimer управлять Вашим циклом получения.
NSTimer таймер общего назначения. Это не таймер, связанный к дисплею. Интервал, и момент, который таймер начал запускать, не имеет никакого отношения к тому, когда происходит вертикальное обновление. Это означает, что простой подход создания таймера в «60,0» Гц обречен перестать работать - таймер сместится в связи с фактической частотой обновления, и Вы отбросите или удвоите кадры. Это также означает, что таймер вызывает приложение в произвольной точке в обновление, сокращающее количество времени, доступное для подготовки получения, если приложение блокирует на вертикальной синхронизации.
Когда вертикальная синхронизация включена в Вашем приложении OpenGL, в течение каждой длительности повтора, когда таймер стреляет, приложение начинает подготавливать данные к следующему кадру; когда получение сделано, блоки приложений, ожидающие следующей вертикали, восстанавливают. Таймер не может стрелять, пока подкачка не завершается. Чтобы позволить Вашему приложению иметь достаточно времени для представления команд рисования, Вы захотите позволить огню таймера, как только приложение возвращается к циклу событий. Вспомните, что нет никакой корреляции вообще между тем, когда таймер стреляет и когда происходит вертикальное обновление. При использовании интервала довольно достижения (такого как 0,0167 секунды для получения 60 кадров в секунду), таймер может стрелять где угодно в длительность повтора, давая приложению остаток от кадра для подготовки данных к следующему кадру. Необходимо установить таймер в очень маленький интервал, такой как 0,001 секунды или 1 000 кадр/с. Это удостоверяется, что огни таймера прямо после подкачки завершаются, давая Вашему приложению всю длительность повтора, чтобы сделать получение.
Заметьте, что, если вертикальная синхронизация не включена в Вашем приложении OpenGL путем создания таймера с исключительно маленьким интервалом (таким как 0,001 секунды или 1 000 кадр/с), приложение запишет много процессорного времени, просто исчерпывающего таймер, даже при том, что цикл получения даже не завершил свое последнее выполнение. Результирующий эффект этого будет умерен к серьезным проблемам производительности, в зависимости от того, насколько занятый цикл получения и как быстро машина может обслужить таймер. Однако при синхронизации буферных подкачек с частотой кадровой развертки этот маленький временной интервал не перегрузит конвейер, потому что таймер ничего не делает когда блоки приложений во время подкачки независимо от временного интервала, таким образом не занимая дополнительного процессорного времени.
Выборка перечисления 2 рисование таймера цикла. (Удостоверьтесь, что Вы включаете вертикальную синхронизацию, иначе, этот таймер с маленьким временным интервалом 0,001 секунд (1 000 кадр/с) зажжет процессорное время.)
// Synchronize buffer swaps with vertical refresh rate |
- (void)prepareOpenGL |
{ |
GLint swapInt = 1; |
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; |
} |
// Put our timer in -awakeFromNib, so it can start up right from the beginning |
-(void)awakeFromNib |
{ |
renderTimer = [NSTimer timerWithTimeInterval:0.001 //a 1ms time interval |
target:self |
selector:@selector(timerFired:) |
userInfo:nil |
repeats:YES]; |
[[NSRunLoop currentRunLoop] addTimer:renderTimer |
forMode:NSDefaultRunLoopMode]; |
[[NSRunLoop currentRunLoop] addTimer:renderTimer |
forMode:NSEventTrackingRunLoopMode]; //Ensure timer fires during resize |
} |
// Timer callback method |
- (void)timerFired:(id)sender |
{ |
// It is good practice in a Cocoa application to allow the system to send the -drawRect: |
// message when it needs to draw, and not to invoke it directly from the timer. |
// All we do here is tell the display it needs a refresh |
[self setNeedsDisplay:YES]; |
} |
История версии документа
| Дата | Примечания |
|---|---|
| 02.01.2013 | Удаленный дополнительное [в Перечислении 2. Фиксированный ссылка. |
| 01.05.2009 | Исправленный опечатка. |
| 23.12.2008 | Добавленное использование CVDisplayLink. Включение, на которое указывают, VSYNC при использовании NSTimer. |
| 04.10.2004 | Новый документ, что использование Базовых ссылок Видеодисплея (CVDisplayLink) или таймеров Какао (NSTimer) для управления циклом рендеринга OpenGL |