domingo, 15 de noviembre de 2009

Como actualiza la pantalla el Flash Player...

El Flash Player(FP) es la maquina virtual que nos permite ejecutar nuestras aplicaciones hechas en Flash, Flex o Actionscript Puro (incluso ejecuta codigo en otros lenguajes cuyo bytecode es "traducido" a un lenguaje que el player entienda, pero eso es para otro post)
Una de los principales usos que se hacen del FP es la parte grafica que ha provisto por muchisimo tiempo excelentes herramientas para desarrolladores y dise~nadores para crear contenido totalmente fuera de lo normal. De hecho podriamos pensar que lo que ha hecho grande y famoso a la plataforma flash es la capacidad de crear aplicaciones que brindan al usuario excelentes experiencias.
Pero... esas excelentes ideas y deseos de buenas experiencias pueden verse truncados si nuestras aplicaciones no estan haciendo uso correcto del FP, sobre todo en la forma en que este maneja la actualizacion de la pantalla.

Ejecucion:

* Definicion: Frame Rate: es la cantidad maxima de veces que por segundo el FP revisara si hay cambios en nuestra aplicacion que se tengan que dibujar en la pantalla. Esto no significa que si tenemos un framerate de 24 fps(frames per second) es que el FP vaya a revisar si hay cambios, 24 veces en un segundo. Esto quiere decir solamente que si todo sale bien, si nada del codigo que uno como desarrollador escribio demora mucho tiempo, el intentara revisar esa cantidad de veces, pero a veces no sera posible.

- Nuestra aplicacion es cargada dentro del FP y es agregada al stage.
- FP ejecuta el codigo inicial de nuestro Frame.
- FP ejecuta algun event listener que sea despachado.
- FP ejecuta el proceso de rendereo (o actualizacion de la pantalla), que basicamente revisa si el codigo ejecutado previamente realizo algun cambio en la parte grafica que deba ser dibujado en la pantalla. Cuando se hace ese chequeo se despacha el evento Event.ENTER_FRAME.
- Si hay cambios, actualiza la pantalla.
- FP espera hasta que se vuelva a ejecutar el chequeo. Si algun event listener es ejecutado (eventos del mouse o del teclado, etc) los ejecuta.
- Chequea si hay cambios, si los hay actualiza la pantalla.
- Repitir.

Conclusiones importantes:

- Este ciclo de chequeo se repite infinitamente durante la ejecucion de nuestra aplicacion, sea que tengamos 1 o tengamos miles de Frames, el FP solo le importa saber si hay cambios que se tengan que reflejar en la pantalla y el gustosamente se encargara de chequearlo. Cambios pueden ser: dibujar algo (graphics.drawLine,etc), mover el X o Y o el width o height de nuestros componentes o los componentes dentro de nuestra aplicacion, cambiar el color de algo, etc.
- Los cambios que hagamos dentro de una funcion a nuestros componentes visuales no se reflejaran inmediatamente en pantalla (aunque a veces parezca que si), sino hasta que nuestro codigo termine de ejecutarse y el cambio proceso de rendereo se ejecute. Por ejemplo si tenemos algo como: comp.width = 100; comp.height = 200; dentro de un metodo, esto NO quiere decir que FP vayaa ejecutar nuestro codigo asi:
1. Cambie el width.
2. Dibuje la pantalla con el nuevo width.
3. Cambie height.
4. Redibuje la pantalla con nuevo height
El FP ejecutara el redibujado de la pantalla hasta que nuestro codigo se termine de ejecutar.
- Aunque nuestras aplicaciones no tengan muchos frames (2 en el caso de Flex) o solo 1 el evento ENTER_FRAME se va a despachar multiples veces porque el FP va a revisar si hay en cambios en la pntalla que deba actualizar. De ahi que el evento ENTER_FRAME sea utilizado multiples veces para ejecutar animaciones o procesos que deben ejecutarse infinitamente dentro de un mismo frame. Essto quiere decir que si tenemos un proceso que debe ejecutarse infinitamente, no debemos meterlo dentro de un ciclo infinito, digamos while(true), sino dentro de este event listener.
- El ciclo de rendereo, es realizado automaticamente por el FP.
- Si se esta ejecutando codigo de la aplicacion, el proceso de rendereo de la pantalla no se va a ejecutar y viceversa. Estos dos son exclusivos, no se puede chequear mientras se ejecuta codigo (funciones o event handlers) y no se puede ejecutar ningun metodo o event handler si se esta haciendo el chequeo. Por ejemplo, si nuestro frame rate es de 10 fps quiere decir que el FP va a revisar en teoria cada 100ms (1000 ms = 1 segundo, 1000 / 10 = 100 :) ) si hay que hacer una actualizacion. Pero como mencione arriba el framerate no necesariamente se va a ejecutar al mismo tiempo siempre. Si tenemos una rutina que recorre un Array con 1000 objetos por dentro y a cada objeto se le ejecuta algun metodo, una validacion y se le setea algunos valores, puede ser que esa funcion dure mas de los 100ms que esperaria el FP para revisar si hay cambios. Esto quiere decir que si la funcion demora 900ms, el FP no revisara si habra cambios hasta el siguiente segundo, no cuando se cumplan 200ms desde el primer chequeo.
- Si tenemos rutinas de codigo muy grandes, muy pesadas nuestra pantalla podria llegar a "congelarse" o verse "chunky", como pegada. Esto es porque como se expuso arriba la actualizacion de la pantalla no se va a realizar hasta que se termine de ejecutar nuestor codigo. Entre mas duren nuestras rutinas mas va a durar en refrescarse nuestra pantalla y peor puede ser la experiencia del usuario.
- Hay un tiempo maximo de ejecucion de codigo que por default es de 15segs. Si por ejemplo tenemos una clase y hacemos un ciclo while(true) en el constructor, podemos esperar 15 segs y ver como nuestra aplicacion va a mostrarnos un error. Esto es porque el tiempo maximo de ejecucion de una funcion fue excedida.

Formas de Optimizacion:

- Si tenemos rutinas muy grandes y que demoran mucho tiempo, lo mejor seria dividir esas rutina en varias para ejecutarse en distintos tiempos, algo como lo que se puede hacer con Threads en otros lenguajes. Recordemos que aunque el FP maneja Threads internamente, nosotros como desarrolladores no tenemos acceso a ellos y solamente podemos emularlos.
- Si utilizamos Flex, hagamos uso de los componentes creados en el framework que en su mayoria estan optimizados para este ciclo de actualizacion de pantalla. Si vamos a hacer nuestros propios componentes, sigamos las reglas y hagamos un override de los metodos sugeridos por Adobe (esto para FLEX 3) como commitProperites y updateDisplayList entre otros. Ambos metodos mencionados antes tienen su razon de ser una funcion especifica para hacer que se ejecuten las cosas de una mejor manera.
- Si no utilizamos Flex, podemos hacer uso del evento Event.RENDER. Este metodo es despachado por el FP exactamente antes de dibujar los cambios en la pantalla. Podemos recibir este evento haciendo un addEventListener normal, pero ademas de eso tenemos que llamar a stage.invalidate(). Esto forzara al evento a ser despachado. Ahora en que nos ayuda este evento? Pues al ser despachado justamente antes de ser actualizada nuestra pantalla, podemos revisar en nuestro listener, si nuestra clase tiene algun cambio en la pantalla. Si lo tiene, llama a la funcion que dibuje o haga los cambios que se quieran hacer en pantalla. Si no hay cambios simplemente no hacemos nada. Eso nos permite forzar al FP redibujar lo que cambio SOLAMENTE cuando haya cambios que de verdad alteren nuestra pantalla y forzar a que la actualizacion de la pantalla se haga solamente hasta que se tenga que hacer. Podemos hacer mas uso de esto, si ponemos setters en nuestros componentes que se encargue de registra que hay cambios en nuestro objeto en un flag booleano. En lugar de llamar dentro del setter al metodo que se encarga de dibujar nuestros graficos o mover nuestros componentes cada vez que se cambia una variable. El event handler del metodo renderer, consultara si nuestro flag esta activado y si lo esta ahi llamara a nuestro metodo. Asi en lugar de llamar el metodo que dibuja nuestra pantalla cada vez que se cambia una variable, este se ejecutara una sola vez y exactamente antes de que se ejecute el redibujado de la pantalla. Esto es muy parecido al proceso de optimizacion que tienen los componentes en Flex.

Otros recursos:
El excelente libro de Colin Moock, Essential Actionscript 3.0, especialmente el capitulo 23
Racetrack: Excelente analisis de como funciona el proceso de rendereo en el Flash Player.