opengl-es uikit core-animation
https://www.dropbox.com/s/vznzccibhlf8bon/simple_sample.ziphttps://www.dropbox.com/s/kzybsyu10825ikw/ios-opengl-scrollview-test.zip

opengl es - Cómo sincronizar el dibujo de OpenGL con las actualizaciones de UIKit



opengl-es core-animation (7)

En nuestra aplicación tenemos UIScrollView encima de CAEAGLLayer. UIScrollView contiene algunas vistas UIV (rectángulos rojos). En CAEAGLLayer dibujamos rectángulos blancos. Los centros de rectángulos blancos son los mismos que los centros de rectángulos rojos. Cuando UIScrollView se desplaza, actualizamos las posiciones de los rectángulos blancos en CAEAGLLayer y los representamos.

Estamos obteniendo el resultado esperado: los centros de los rectángulos blancos son siempre los mismos que los centros de los rectángulos rojos.

Pero no podemos sincronizar las actualizaciones de CAEAGLLayer con el movimiento de las vistas dentro de UIScrollView. Tenemos algún tipo de distorsión: los rectángulos rojos van detrás de los rectángulos blancos.

Hablando en términos generales, realmente queremos que CAEAGLLayer se retrase junto con el UIScollView.

Hemos preparado código de ejemplo. Ejecútelo en el dispositivo y desplácese, y verá que los rectángulos blancos (dibujados por OpenGL) se mueven más rápido que los rojos (vistas UIV normales). OpenGL se está actualizando dentro de scrollViewDidScroll: llamada delegada.

https://www.dropbox.com/s/kzybsyu10825ikw/ios-opengl-scrollview-test.zip

Se comporta de la misma manera incluso en el simulador de iOS, solo eche un vistazo al video: http://www.youtube.com/watch?v=1T9hsAVrEXw

Rojo = UIKit, Blanco = OpenGL

El código es:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView { // reuses red squares that are gone outside thw bounds [overlayView updateVisibleRect:CGRectMake(...)]; // draws white squares using OpenGL under the red squares [openGlView updateVisibleRect:CGRectMake(...)]; }

Editar:

El mismo problema se puede demostrar fácilmente en una muestra mucho más simplificada. El xcodeproj de trabajo se puede encontrar en:

https://www.dropbox.com/s/vznzccibhlf8bon/simple_sample.zip

El proyecto de muestra básicamente dibuja y anima un conjunto de cuadrados BLANCOS en OpenGL y hace lo mismo para un conjunto ROJO de UIViews. El retraso se puede ver fácilmente entre los cuadrados rojos y blancos.


A falta de una respuesta adecuada me gustaría compartir mis pensamientos:

Hay dos formas de dibujar involucradas: Core Animation (UIKit) y OpenGL. Al final, todo el dibujo lo realiza OpenGL, pero la parte de Core Animation se representa en backboardd (o Springboard.app, antes de iOS 6) que sirve como una especie de servidor de ventana.

Para hacer que esto funcione, el proceso de su aplicación serializa la jerarquía de capas y los cambios en sus propiedades y pasa los datos a backboardd, que a su vez procesa y compone las capas y hace visible el resultado.

Al mezclar OpenGL con UIKit, el renderizado CAEAGLLayer (que se realiza en su aplicación) tiene que estar compuesto con el resto de las capas de Core Animation. Supongo que el búfer de procesamiento utilizado por CAEAGLLayer se comparte de alguna manera con backboardd para tener una forma rápida de transferir el procesamiento de su aplicación. Este mecanismo no necesariamente tiene que estar sincronizado con las actualizaciones de Core Animation.

Para resolver su problema, sería necesario encontrar una forma de sincronizar la representación de OpenGL con la serialización de la capa Core Animation y transferirla a backboardd. Lo siento por no poder presentar una solución, pero espero que estos pensamientos y conjeturas te ayuden a comprender la razón del problema.



De hecho, no puedes sincronizarlos usando las API actuales. MobileMaps.app y Apple Map Kit usan propiedad privada en CAEAGLLayer de asynchronous para solucionar este problema.

Aquí está el radar relacionado: http://openradar.appspot.com/radar?id=3118401


Después de cavar un poco me gustaría extender mi respuesta anterior:

Su representación de OpenGL se realiza inmediatamente desde el método scrollViewDidScroll: mientras que el dibujo de UIKit se realiza más tarde, durante las actualizaciones normales de transacción de CAT desde el runloop.

Para sincronizar las actualizaciones de UIKit con la representación de OpenGL, simplemente encierre ambas en una transacción explícita y vacíelo para obligar a UIKit a confirmar los cambios en backbaordd inmediatamente:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView { [CATransaction begin]; [overlayView updateVisibleRect:CGRectMake(...)]; [openGlView updateVisibleRect:CGRectMake(...)]; [CATransaction flush]; // trigger a UIKit and Core Animation graphics update [CATransaction commit]; }


En iOS 9, CAEAGLLayer tiene la propiedad presentsWithTransaction que sincroniza los dos.


Estoy tratando de resolver exactamente el mismo problema. Intenté todos los métodos descritos anteriormente, pero nada era aceptable.

Idealmente, esto se puede resolver accediendo al contexto de composición compilación final de Core Animation. Pero no podemos acceder a la API privada.

Otra forma es transferir el resultado de GL a CA. Intenté esto leyendo píxeles de frame-buffer para enviar a CALayer que era demasiado lento. Alrededor de más de 200 MB / s de datos deben transferirse. Ahora estoy tratando de optimizar esto. Porque esta es la última forma en que puedo intentarlo.

Actualizar

Todavía pesado, pero leer frame-buffer y configurarlo en CALayer.content funciona bien y muestra un rendimiento aceptable en mi iPhone 4S. De todos modos, más del 70% de la carga de la CPU se utiliza solo para esta operación de lectura y configuración. Especialmente glReadPixels I, la mayoría de ellos están esperando el tiempo para leer datos de la memoria de la GPU.

Actualización2

Renuncié al último método. Su costo es casi pura transferencia de píxeles y sigue siendo demasiado lento. Decidí dibujar todos los objetos dinámicos en GL. Sólo se dibujarán algunas vistas estáticas emergentes con CA.


Para sincronizar el dibujo de UIKit y OpenGL, debes intentar renderizar donde Core Animation espera que dibujes, es decir, algo como esto:

- (void)displayLayer:(CALayer *)layer { [self drawFrame]; } - (void)updateVisibleRect:(CGRect)rect { _visibleRect = rect; [self.layer setNeedsDisplay]; }