Случайная авария в приложении iPhone OpenGL при навигации

Я работаю над приложением iPhone, которое представляет собой гибридный OpenGL ES и обычный пользовательский интерфейс iPhone. Это означает, что есть EAGLView приветствие пользователя, затем некоторые регулярные UIView, которые нажимаются на него (в качестве корневого контроллера есть UINavigationController).

У меня случается случайный (но очень частой) сбой при навигации с подвью. Вот трассировка стека (... цензура...) из сборки Release, но в Debug она так же сбой.

#0  0x006863d0 in GetFBOBuffers ()
#1  0x00660120 in TerminateScene ()
#2  0x00660314 in FlushScene ()
#3  0x00660cd4 in FlushHW ()
#4  0x0066a6a0 in GLESPresentView ()
#5  0x323533a4 in -[EAGLContext presentRenderbuffer:] ()
#6  0x000026c0 in -[EAGLView presentFramebuffer] (self=0x11ce60, _cmd=<value temporarily unavailable, due to optimizations>) at (...)/Classes/EAGLView.m:157
#7  0x00004fdc in -[(...)ViewController drawFrame] (self=<value temporarily unavailable, due to optimizations>, _cmd=<value temporarily unavailable, due to optimizations>) at (...)    ViewController.m:380
#8  0x336ebd9a in __NSFireTimer ()
#9  0x323f54c2 in CFRunLoopRunSpecific ()
#10 0x323f4c1e in CFRunLoopRunInMode ()
#11 0x335051c8 in GSEventRunModal ()
#12 0x324a6c30 in -[UIApplication _run] ()
#13 0x324a5230 in UIApplicationMain ()
#14 0x0000214c in main (argc=1, argv=0x2ffff568) at (...)/main.m:14

Вот список вещей, которые я знаю:

  • Мое приложение не получает предупреждения о памяти.
  • Мое приложение не идентифицирует утечку в разделе "Инструменты".
  • Отсутствие сбоя в симуляторе, но иногда очень заметное отставание.
  • Значительное количество выпущенных данных в инструментах /OpenGL/ResourceBytes происходит непосредственно перед сбоем.
  • Я использую как VBOs, так и массивы vertex/texcoord/normals.

Поэтому я знаю, что это должны быть какие-то данные, которые освобождаются или уничтожаются, но я не знаю, как их найти. Любые советы и трюки будут оценены, -)

ОБНОВЛЕНИЕ:

После установки некоторых точек останова, перемещаясь по стеку, выталкивая различные переменные, я нашел причину сбоя, но еще не источник.

В EAGLView в методе presentFramebuffer, где и когда происходит сбой, значение ivar colorRenderBuffer равно 0, если я могу верить gdb, даже если пытаюсь остановиться, когда он 0 не работает.

Кажется, что вызов deleteFrameBuffer из layoutSubviews не соответствует createFramebuffer.

ОБНОВЛЕНИЕ 2:

Множество точек прерывания позже... Я нашел неправильную ситуацию: [EAGLView layoutSubviews] вызывается в середине drawFrame! Таким образом, буферы удаляются во время использования... BAM!

Теперь, как я могу это исправить?

Ответ 1

Я еще не нашел "правильное" решение, но я добавил обходное решение.

В presentFramebuffer я устанавливаю булевое значение вокруг рендеринга:

if (context)
{
    isRendering_PATCH_VARIABLE = YES;

    [EAGLContext setCurrentContext:context];

    glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);

    success = [context presentRenderbuffer:GL_RENDERBUFFER_OES];

    glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0);

    isRendering_PATCH_VARIABLE = NO;
}

и в deleteFramebuffer, я проверяю это логическое значение:

if (isRendering_PATCH_VARIABLE)
{
    NSLog(@"GOTCHA - CRASH AVOIDED");   
}

if (context && !isRendering_PATCH_VARIABLE)
{
    // ...
}

У него нет побочных эффектов (например, сломанный дисплей и т.д.), поэтому я оставлю его так на данный момент.

Ответ 2

Здесь дикое предположение основано на том, что я сделал недавно.

Что я сделал, так это разбить стадию загрузки. Я хотел показать индикатор выполнения, когда я загрузил несколько ресурсов - это медленный процесс, поэтому я хотел бы предоставить некоторые отзывы пользователей.

Мой первый шаг был похож на пример OpenGL ES, снабженный Xcode, в котором я назвал init в ES? Renderer. Но после этого я остановил процесс, чтобы загрузить другие ресурсы.

Чтобы сократить длинную историю, из-за моей переупорядочивания кода, [EAGLView layoutSubviews] никогда не вызывался после моей инициализации. Я не потерпел крушение, но после этого ничего не произошло.

Мне нужно было закончить инициализацию контекста OpenGL и загрузку всех моих данных, мне пришлось вручную вызвать [EAGLView layoutSubviews]. Это, казалось, исправить вещи для меня.

Возможно, вам нужно попробовать что-то подобное. После того, как вы инициализируете свой контекст и данные OpenGL, вызовите [EAGLView layoutSubviews], прежде чем вы войдете в свои подпрограммы рисования. Возможно, это снова остановит этот вызов, снова появляясь на вашем этапе рендеринга и сбой.

Ответ 3

Удар в темноте: ваше устройство становится низким в памяти, приложение получает предупреждение о памяти, а контроллер, ответственный за просмотр GL, высвечивает вид, с которым вы не считаете? Устранена ли проблема, если вы подавите стандартную didReceiveMemoryWarning?

- (void) didReceiveMemoryWarning { /* nothing we can do, sorry */ }

... или, может быть, вы вызываете OpenGL из другого потока, который не имеет контекста?

Ответ 4

У меня была эта проблема, и я некоторое время использовал вашу работу. Однако я заметил, что авария произошла только при изменении ориентации.

Я изменил свой контроллер корневого представления, чтобы приостановить отображение в WillRotate... и возобновить его в функции делегирования DidRotateFrom...

Это устранило проблему, и мне больше не нужно работать с хаком.

К сожалению, это звучит так, как будто ваша проблема немного отличается, но я думал, что я опубликую это на всякий случай.