bucle de juego de Android vs actualización en el hilo de representación

Estoy haciendo un juego para Android y actualmente no estoy obteniendo el rendimiento que me gustaría. Tengo un bucle de juego en su propio hilo que actualiza la posición de un objeto. El hilo renderizado atravesará estos objetos y los dibujará. El comportamiento actual es lo que parece ser un movimiento entrecortado / desigual. Lo que no puedo explicar es que antes de poner la lógica de actualización en su propio hilo, lo tenía en el método onDrawFrame, justo antes de que el gl llame. En ese caso, la animación fue perfectamente suave, solo se vuelve entrecortada / irregularmente específicamente cuando trato de acelerar mi ciclo de actualización a través de Thread.sleep. Incluso cuando permito que el subproceso de actualización se vuelva loco (sin suspensión), la animación es fluida, solo cuando está implicado Thread.sleep afecta la calidad de la animación.

He creado un proyecto de esqueleto para ver si podía recrear el problema, a continuación se encuentran el ciclo de actualización y el método onDrawFrame en el renderizador:Actualizar Loop

    @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();
            }
        }
    }
}

Y aqui esta mionDrawFrame método:

        @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);
}

He revisado la fuente de la réplica de la isla y él está ejecutando su lógica de actualización en un subproceso aparte, además de controlarlo con Thread.sleep, pero su juego se ve muy suave. ¿Alguien tiene alguna idea o alguien ha experimentado lo que estoy describiendo?

--- EDITAR: 1/25/13 ---
He tenido algo de tiempo para pensar y he suavizado este motor de juego considerablemente. La forma en que logré esto podría ser blasfema o insultante para los programadores de juegos reales, así que por favor siéntase libre de corregir cualquiera de estas ideas.

La idea básica es mantener un patrón de actualización, dibujar ... actualizar, dibujar ... mientras se mantiene el tiempo delta relativamente igual (a menudo fuera de su control). Mi primer curso de acción fue sincronizar mi renderizador de tal manera que solo se dibujara después de que se le notificara que podía hacerlo. Esto se ve algo como esto:

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

Cuando termino mi lógica de actualización, llamo a drawLock.notify (), liberando el subproceso de representación para dibujar lo que acabo de actualizar. El propósito de esto es ayudar a establecer el patrón de actualización, dibujo ... actualización, dibujo ... etc.

Una vez que lo implementé, fue considerablemente más suave, aunque todavía estaba experimentando saltos ocasionales en movimiento. Después de algunas pruebas, vi que tenía varias actualizaciones entre llamadas de ondrawFrame. Esto causó que un cuadro muestre el resultado de dos (o más) actualizaciones, un salto más grande de lo normal.

Lo que hice para resolver esto fue limitar el tiempo delta a algún valor, digamos 18 ms, entre dos llamadas onDrawFrame y almacenar el tiempo adicional en un resto. Este resto se distribuiría a los deltas de tiempo subsiguientes en las próximas actualizaciones si pudieran manejarlo. Esta idea evita todos los saltos largos repentinos, esencialmente suavizando un pico de tiempo en múltiples cuadros. Hacer esto me dio grandes resultados.

La desventaja de este enfoque es que durante un poco de tiempo, la posición de los objetos no será precisa con el tiempo, y en realidad se acelerará para compensar esa diferencia. Pero es más suave y el cambio de velocidad no es muy notable.

Finalmente, decidí volver a escribir mi motor con las dos ideas anteriores en mente, en lugar de parchear el motor que originalmente había creado. Hice algunas optimizaciones para la sincronización de hilos que quizás alguien podría comentar.

Mis hilos actuales interactúan así:
-Actualizar hilo actualiza el búfer actual (sistema de búfer doble para actualizar y dibujar simultáneamente) y luego le dará este búfer al renderizador si se ha dibujado el marco anterior.
-Si el cuadro anterior aún no se ha dibujado, o se está dibujando, el hilo de actualización esperará hasta quehacer hilo Se lo notifica que ha dibujado.
-Hacer hilo espera hasta que sea notificado poractualizar hilo que se ha producido una actualización.
-Cuando elhacer hilo dibuja, establece una "última variable dibujada" que indica cuál de los dos búferes dibujó por última vez y también notifica al subproceso de actualización si estaba esperando que se dibujara el búfer anterior.

Eso puede ser un poco complicado, pero lo que se está haciendo es permitir las ventajas de los subprocesos múltiples, ya que puede realizar la actualización para el fotograma n mientras que el fotograma n-1 se está dibujando al mismo tiempo que se evitan múltiples iteraciones de actualización por fotograma si el procesador está tomando una largo tiempo. Para explicar con más detalle, este escenario de actualización múltiple se maneja mediante el bloqueo del subproceso de actualización si detecta que el búfer LastDrawn es igual al que se acaba de actualizar. Si son iguales, esto indica al subproceso de actualización que el marco anterior aún no se ha dibujado.

Hasta ahora estoy obteniendo buenos resultados. Déjeme saber si alguien tiene algún comentario, estaría feliz de escuchar sus pensamientos sobre cualquier cosa que esté haciendo, bien o mal.

Gracias

Respuestas a la pregunta(2)

Su respuesta a la pregunta