Технические вопросы и ответы QA1385

Ведущие циклы рендеринга OpenGL

Q: Как я управляю циклом получения своего Какао приложение OpenGL?

A: На Mac OS X 10.4 и позже, приложения должны использовать Базовую ссылку Видеодисплея (CVDisplayLink) управлять циклом получения Какао приложение OpenGL. Для совместимости с Mac OS X 10.3 и ранее, они должны использовать таймер Какао (NSTimer).

Приложения OpenGL должны избежать кадра рвущиеся и пропадающие впустую ресурсы, получающие пиксели, которые никогда не будет видеть пользователь. Чтобы сделать это, приложения не должны рисовать быстрее, чем дисплей может обновить и должен подкачать или нарисовать во время вертикального интервала обратного хода луча. CVDisplayLink обеспечивает отдельный высокоприоритетный поток для уведомления приложения, когда данному дисплею будет нужен каждый кадр, удостоверяясь, что обновление не наложится с ранее представленным кадром. То, как часто кадр требуют, основывается на частоте обновления дисплея, в настоящее время связываемого со ссылкой дисплея. Перечисление 1 показывает пример того, как использовать CVDisplayLink управлять Вашим циклом получения. Для получения дополнительной информации о 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