2016-06-11 2 views
0

Моя цель - создать собственное приложение Cocoa для таргетинга на OSX 10.4+, которое отображает OpenGL, чтобы порт игры, которую я создаю. Моя проблема заключается в том, что после технических нот Apple (см. Ссылки ниже) я не могу понять, как отделить обновление и рендеринг в Cocoa. Это мой вопрос: как обновление приложения Cocoa может отличаться от другого? Другими словами, рендеринг Cocoa основан на событиях, и я не хочу ждать события, чтобы обновить мою симуляцию.Рендеринг и обновление в OS X с использованием OpenGL

Вот мясо, что я работаю:

static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) 
    { 
     CVReturn result = [(__bridge GameView*)displayLinkContext getFrameForTime:outputTime]; 
     return result; 
    } 

    - (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime 
    { 
     //TODO: find somewhere better to put this. 
     //TODO: the magic number is not doing what I expect it to. 
     float dt = (outputTime->videoTime - lastCapturedTime)/36000000.0f; 
     lastCapturedTime = outputTime->videoTime; 
     update(dt); 

     NSOpenGLContext *currentContext = [self openGLContext]; 
     [currentContext makeCurrentContext]; 

     // must lock GL context because display link is threaded 
     CGLLockContext((CGLContextObj)[currentContext CGLContextObj]); 

     glClearColor(0, 0, 0, 0); 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
     render(); 
     glFlush(); 

     [currentContext flushBuffer]; 

     CGLUnlockContext((CGLContextObj)[currentContext CGLContextObj]); 
     return kCVReturnSuccess; 
    } 

Что мне не нравится в коде выше, что делает и обновление произойдет на том же событии, и намерение мероприятия является то, что ОС сказал мое приложение должно отобразить фрейм.

В Windows, ставя этот код ниже в WinMain(), даст мне именно то, что я ищу.

while(true) { 
    process_events(); //PeekMessage, handle WM_ events 
    int elapsedTime = GetElapsedSeconds(); 
    prepare(elapsedTime); 
    if (fps_lock(elapsedTime, 60)) { 
     render(); 
    } 
    swapBuffers(); 
} 

Вот что я смотрел (я вывесил бы больше, чем просто две ссылки, потому что у меня есть куча исследований, я сделал, но мой низкий респ помешал мне сделать это):

заранее спасибо за указание мне то, что я, возможно, пропустили, или же от отметки о полностью.

+0

Много информации здесь для основного вопроса. вы можете захотеть упростить его, например «Как визуализировать и обновлять с разной скоростью»? и укажите пример кода. Удачи. :-) –

ответ

0

Я предполагаю, что update() и prepare() аналогичны и только обновляют физику и т. Д., И не делают никакого OpenGL. И вы хотите сделать это как можно быстрее/чаще?

Если ваш код является потокобезопасным, вы можете сделать это на основе таймера. Вы можете использовать Grand Central Dispatch timer source в последовательной очереди. Аналогично, вы можете использовать NSTimer в цикле выполнения потока, который запускает цикл выполнения. Теперь основной поток приложения запускает цикл выполнения, а основная очередь отправки - это очередная очередь, но вы можете не использовать основной поток/очередь, потому что это будет мешать обработке событий пользовательского интерфейса. Таким образом, вы можете захотеть использовать пользовательскую очередь или поток, который вы создаете сами, и выполните запуск цикла выполнения.

Если вам нужно синхронизировать рендеринг с обновлением, вы можете использовать примитив синхронизации, такой как семафор отправки, или вы можете делать такие вещи, как синхронное отправку рендеринга в одну и ту же последовательную очередь, на которую нацеливается источник отправки таймера. Или используйте -performSelector:onThread:withObject:waitUntilDone:, чтобы нацелить свою нить.

Кстати, в вашем псевдокоде Windows, почему вы меняете буферы на проходы, где вы не отображались?

+0

Кен, ваше предположение о методах обновления/подготовки правильное (они аналогичны). И да, я не хочу, чтобы игра сидела и обновлялась только при таких событиях, как ввод мыши или клавиатуры. Это заставило меня более внимательно изучить мой код, и я увидел, что они меняют некоторые данные вершин в динамическом буфере рисования, используя glBindBuffer() и glBufferSubData(). Никаких других вызовов GL не производится. Процедура render() вызывает glBindBuffer() снова, а затем вызывает шейдерные программы. Кроме того, о псевдокоде windows, swap_buffers() должен быть в этом if-блоке после render(). –

+0

Вы можете потенциально сделать OpenGL из 'update()', следуя нескольким правилам: используйте только данный контекст из одного потока за раз, убедитесь, что контекст стал текущим для потока, если вы не уверены в этом (например, очереди отправки могут запускать каждую задачу в другом потоке), возможно, лучше всего очистить контекст для потока перед возвратом, если вы не являетесь владельцем потока. Вы можете использовать общие контексты для управления объектами GL в одном потоке и визуализации на другом. Обязательно используйте 'glFlush()' или 'glFlushRenderAPPLE()', чтобы убедиться, что изменения «видимы» в другом потоке. –

+0

Кен, я хочу поблагодарить вас за ваш ответ. Сейчас у меня все работает гладко, и я вполне доволен реакцией таймера. У меня есть только один последний вопрос: в приложении Cocoa, использующем GCD, есть ли лучшая практика для того, где начинать таймер? Я вызываю 'dispatch_resume()' из моего подкласса NSOpenGLView' в методе 'prepareOpenGL()'. Кажется, что все работает нормально, но мне интересно, предписывает ли Apple конкретное событие для ответа на это, это хорошо для запуска потоков? Я не сталкивался с этим ни в одном из документов разработчика. –

Смежные вопросы