Здравствуйте. Проблема, с которой я столкнулся, не тривиальна совсем, но выход найти очень нужно.
У меня есть свой игровой движок, который имеет свой Main Loop - цикл, в котором все время "вертится" приложение. По дизайну, "разорвать" этот цикл нельзя - приложение живет, пока я в этом цикле, и умирает, когда я из него выхожу. Примитивно это можно представить так:
do { опросить ввод; обработать логику; нарисовать; } while (!quit)
Эту структуру разрывать нельзя, я не могу вызвать внутренности отдельно, например, по таймеру или еще как-либо.
С виндой и MacOS такая структура прокатывает: винда не навязывает своего цикла, а в MacOS цикл можно "разорвать", воспользовавшись недокументированными возможностями :shuffle:
Но в Cocoa Touch так не получается. В UIKit свой модальный цикл, и разрывать его она никому не дает, а два модальных цикла в одном потоке не уживаются. Я решил эту проблему путем создания нового потока - Cocoa крутит свой цикл в своем, а моя игра - в своем, и общение между ними происходит через performSelectorOnMainThread (сама Cocoa Touch однопоточна).
Все более-менеее работало, пока не потребовалось внедрить сторонний компонент, который, также как и моя игра, рисует в OpenGL. Они начали "бодаться" и в результате начались падения. Помучавшись, я понял, что лучше всего мне будет избавиться от двух потоков. Но как это сделать?
У меня две идеи, но в каждой есть вопросы:
1. Как-то разорвать модальный цикл Cocoa. Для этого мне нужно знать устройство их главной функции, главного цикла. Может, у кого-то есть документация, исходный код?
2. Реализовать цикл движка как сопрограмму, фибер, микропоток. Но в дурацкой IOS set_context-get_context-swap_context не работают! А если пытаться эмулировать фиберы на потоках - не получится ли одно и то же?
Нет ли других решений?
Избавиться от левого компонента не вариант?
Went
> Помучавшись, я понял, что лучше всего мне будет избавиться от двух потоков. Но как это сделать?
> По дизайну, "разорвать" этот цикл нельзя
рви по логике
KaronatoR
> Избавиться от левого компонента не вариант?
BFG обидится, если я избавлюсь от их рекламы :)
optimist32
> рви по логике
Не понял :) Под выражением "по дизайну" имеется в виду, что разорвать совсем нельзя, так уже сделано, переделывать очень трудно (движок использует модальные диалоги).
У меня для тебя плохие новости. Твой движок спроектирован коряво. И скорее все быстрее и лучше будет отрефакторить его сейчас, нежели латать подобные дыры.
Sergio
> Твой движок спроектирован коряво
No U!
Went
Наверно, ты хотел сказать
:)
Но реально встраивание в ранлун UIApplication - это костыль из костылей. Что мешает сделать обновление всего того же по таймеру внутри UIApplicationDelegate?
Sergio
+1
А сделать отдельный контекст для BFGLib не получится? И пусть она им пользуется. Нам еще предстоит интегрировать ее. Вот интересно в чем именно сложность (я пока ее даже не видел )
Sergio
> Наверно, ты хотел сказать...
Нет, я хотел сказать
:)
> Что мешает сделать обновление всего того же по таймеру внутри UIApplicationDelegate?
У меня в движке используются модальные диалоги. То есть код такого вида (псевдокод):
MessageDlg dlg("Are you sure?"); if ( dlg.do_modal( ) == yes) do_something( );
Как ты понимаешь, внутри вызова do_modal крутится МОЙ модальный цикл, который не отдаст управление вызвавшему контексту до тех пор, пока диалог не завершится с каким-то результатом.
Поэтому, в момент отработки этого диалога весь UIKit подвиснет, перестанет отдавать мне клавиатуру, и программа уйдет в бесконечное ожидание.
Стас
> А сделать отдельный контекст для BFGLib не получится?
А она не спрашивает, какой контекст использовать - просто коннектится к корневому вью контроллеру. Теоретически, у нее и должен быть свой контекст, но почему-то коллизии происходят. Везде пишут, что UIKit абсолютно однопоточен, поэтому удивляться не приходится.
Тогда еще хуже. Получается, что у тебя будет висеть не только UIKit, но и на любой другой платформе тоже. Если в этом потоке, например, будут крутиться сокеты - им тоже жопа настанет, и так далее.
Выходов несколько:
1) переделать движок так, чтобы в нем не было элементов, которые требую модальности;
2) переделать движок так, чтобы вся модальность была как-бы внешней по отношению к процессу (проще говоря обновление по таймеру)
3) сделать диалоговое окно платформеннозависимым и использовать модальный UIAlertView.
Sergio
> Получается, что у тебя будет висеть не только UIKit, но и на любой другой
> платформе тоже
На винде и маке все ОК :) С андроидом прозреваю подобную проблему, да.
> Если в этом потоке, например, будут крутиться сокеты - им тоже жопа настанет, и так далее.
Решал без проблем, если правильно тебя понял.
> 1) переделать движок так, чтобы в нем не было элементов, которые требую
> модальности;
Да, это - крайняя мера.
Sergio
> 2) переделать движок так, чтобы вся модальность была как-бы внешней по
> отношению к процессу (проще говоря обновление по таймеру)
Это не понял. Вынести в отдельный поток?
Sergio
> 3) сделать диалоговое окно платформеннозависимым и использовать модальный
> UIAlertView.
Там не только в диалогах проблема. Модальные циклы внутри функций используются повсеместно. Это удобно :)
Went
Как видишь, за такое удобство приходится расплачиваться.
Смысл такой, что вместо
while (modal) do ...
Ты делаешь примерно так:
void Update() {
...
if (modalEndCondition) {
modal = false;
returnControl();
}
}
И вызываешь это обновление, скажем так, по таймеру.
Sergio
Ну, это, фактически, и есть "отказ от модальности" с эмуляцией оной на апдейтах или событиях.
Вести с полей: если вызывать рендер из главного потока, вроде как, коллизий не наблюдается.
Как бы переделать уже готовую игру это немного жесткая мера. И честно говоря не вижу особенного удобства в использовании подобного решения.
Возможность блокировки выполнения основного кода? Ну это можно решить немного другими способами. Например у меня это решается за счет передачи управления только модальным объектам (если они есть ):
bool cGameObject::ProcessInput(bool bModal ) { if( bModal ) { PreProcessInput( ); if( DoProcessInput( ) ) { return true; } } else { PreProcessInput( ); bModal = cGameScene::instance( )->GetModal( ) == this; if( bModal ) { if( DoProcessInput( ) ) { return true; } } }
А вызов окна сообщения делается примерно таким образом:
wstring title = cTextManager::instance()->get_text( "exit_title" ); wstring message = cTextManager::instance( )->get_text( "exit_message" ); cGameMessageBox( title, message, MakeCallback( this, &cGameInit::OnExitGame ), 0 );
Но пока я не видел собственно проблемной библиотеки.. Сказать наверняка что эти советы помогут не могу.
Тема в архиве.