Android-игра цикл против обновления в потоке рендеринга

Я делаю игру для Android и в настоящее время не получаю желаемую производительность. У меня есть игровой цикл в своем собственном потоке, который обновляет позицию объекта. Поток рендеринга будет проходить через эти объекты и рисовать их. Текущее поведение - это то, что кажется прерывистым / неравномерным движением. Что я не могу объяснить, так это то, что до того, как я поместил логику обновления в ее собственный поток, я поместил ее в метод onDrawFrame, прямо перед вызовом gl. В этом случае анимация была совершенно плавной, она становится прерывистой / неровной, особенно когда я пытаюсь ограничить цикл обновления через Thread.sleep. Даже когда я позволяю потоку обновления приходить в бешенство (без сна), анимация плавная, только когда задействован Thread.sleep, это влияет на качество анимации.

Я создал скелетный проект, чтобы посмотреть, смогу ли я воссоздать проблему, ниже приведены цикл обновления и метод onDrawFrame в рендере:Обновить цикл

    @Override
public void run() 
{
    while(gameOn) 
    {
        long currentRun = SystemClock.uptimeMillis();
        if(lastRun == 0)
        {
            lastRun = currentRun - 16;
        }
        long delta = currentRun - lastRun;
        lastRun = currentRun;

        posY += moveY*delta/20.0;

        GlobalObjects.ypos = posY;

        long rightNow = SystemClock.uptimeMillis();
        if(rightNow - currentRun < 16)
        {
            try {
                Thread.sleep(16 - (rightNow - currentRun));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

А вот мойonDrawFrame метод:

        @Override
public void onDrawFrame(GL10 gl) {
    gl.glClearColor(1f, 1f, 0, 0);
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
            GL10.GL_DEPTH_BUFFER_BIT);

    gl.glLoadIdentity();

    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    gl.glTranslatef(transX, GlobalObjects.ypos, transZ);
    //gl.glRotatef(45, 0, 0, 1);
    //gl.glColor4f(0, 1, 0, 0);

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    gl.glVertexPointer(3,  GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, uvBuffer);

    gl.glDrawElements(GL10.GL_TRIANGLES, drawOrder.length,
              GL10.GL_UNSIGNED_SHORT, indiceBuffer);

    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}

Я просмотрел источник на острове-реплике, и он выполняет свою логику обновления в отдельном потоке, а также регулирует ее с помощью Thread.sleep, но его игра выглядит очень гладкой. У кого-нибудь есть идеи или кто-то испытал то, что я описываю?

--- РЕДАКТИРОВАТЬ: 1/25/13 ---
У меня было время подумать и значительно сгладить этот игровой движок. То, как мне это удалось, может быть кощунственным или оскорбительным для реальных программистов, поэтому, пожалуйста, не стесняйтесь исправлять любые из этих идей.

Основная идея заключается в том, чтобы сохранять шаблон обновления, рисовать ... обновлять, рисовать ..., сохраняя при этом разницу времени относительно одинаковой (часто вне вашего контроля). Моим первым шагом было синхронизировать мой рендерер таким образом, чтобы он рисовал только после того, как получил уведомление о том, что ему разрешено это делать. Это выглядит примерно так:

public void onDrawFrame(GL10 gl10) {
        synchronized(drawLock)
    {
        while(!GlobalGameObjects.getInstance().isUpdateHappened())
        {
            try
            {
                Log.d("test1", "draw locking");
                drawLock.wait();
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
        }
    }

Когда я заканчиваю свою логику обновления, я вызываю drawLock.notify (), освобождая поток рендеринга, чтобы нарисовать то, что я только что обновил. Цель этого - помочь установить шаблон обновления, нарисовать ... обновить, нарисовать ... и т. Д.

Как только я это реализовал, все стало намного плавнее, хотя я все еще испытывал случайные скачки в движении. После некоторого тестирования я увидел, что между вызовами ondrawFrame происходит несколько обновлений. Это приводило к тому, что один кадр показывал результат двух (или более) обновлений, больший скачок, чем обычно.

Чтобы решить эту проблему, я ограничил разницу во времени, скажем, 18 мс, между двумя вызовами onDrawFrame и сохранил дополнительное время в остатке. Этот остаток будет распределен в последующие временные разницы в течение следующих нескольких обновлений, если они смогут справиться с этим. Эта идея предотвращает все внезапные длинные прыжки, по существу сглаживая скачок времени в нескольких кадрах. Это дало мне отличные результаты.

Недостатком этого подхода является то, что в течение небольшого времени положение объектов не будет точным со временем, и фактически будет ускоряться, чтобы компенсировать эту разницу. Но он плавнее и изменение скорости не очень заметно.

Наконец, я решил переписать свой двигатель с учетом двух вышеупомянутых идей, а не исправлять двигатель, который я изначально сделал. Я сделал некоторые оптимизации для синхронизации потоков, которые, возможно, кто-то может прокомментировать.

Мои текущие темы взаимодействуют так:
-Обновить ветку обновляет текущий буфер (двойная буферная система для одновременного обновления и отрисовки) и затем передает этот буфер средству визуализации, если предыдущий кадр был нарисован.
-Если предыдущий кадр еще не нарисован или не рисует, поток обновления будет ждать, покавизуализировать поток уведомляет его, что он нарисовал.
-Рендеринг темы ждет, пока не получит уведомление отобновить ветку что произошло обновление.
-Когдавизуализировать поток рисует, он устанавливает «последнюю переменную отрисовки», указывающую, какой из двух буферов был нарисован последним, а также уведомляет поток обновления, если он ожидал отрисовки предыдущего буфера.

Это может быть немного запутанным, но то, что он делает, учитывает преимущества многопоточности в том, что он может выполнять обновление для кадра n, в то время как кадр n-1 рисует, а также предотвращает множественные итерации обновления для кадра, если средство визуализации принимает много времени. Для дальнейшего объяснения этот сценарий множественного обновления обрабатывается блокировкой потока обновления, если он обнаруживает, что буфер lastDrawn равен только что обновленному. Если они равны, это указывает потоку обновления, что предыдущий кадр еще не нарисован.

Пока что я получаю хорошие результаты. Дайте мне знать, если у кого-то есть комментарии, буду рад услышать ваши мысли о том, что я делаю, правильно или неправильно.

Спасибо

Ответы на вопрос(2)

Ваш ответ на вопрос