May 24, 2008

El sistema de renderizado de Flash Player

Renderizar es el proceso de generar en pantalla una imagen a partir de unos datos. El responsable de representar por pantalla todo lo que sucede en una película Flash, tanto en el escenario como en el código, es el sistema de renderizado del runtime.

Debido a que ActionScript es un lenguaje 100% visual, tener un conocimiento profundo de este proceso nos ayudará a la hora de escribir y optimizar nuestro código, y evitará que cometamos errores normalmente difíciles de detectar.

En este artículo veremos cómo funciona.

El sistema de renderizado del Flash Player

El sistema de actualización de pantalla de Flash Player es automatizado y no hay forma de controlarlo.

Su funcionamiento interno está estrechamente ligado a la línea de tiempo de una película: se alcanza un frame, se ejecuta su código y se procesa la imagen. Incluso películas generadas sólo con código, sin ningún frame, o películas compiladas en Flex, que no cuenta con linea de tiempo, están gobernadas por el mismo mecanismo. A efectos del runtime de Flash, ejecuta cada película como si tuviera una linea de tiempo infinita.

El periodo de tiempo para visualizar cada frame viene marcado por el framerate del swf, que se mide en frames por segundo (fps).

Para hacernos mejor una idea, vamos a crear una serie de casos y a apoyar la explicación con gráficos y ejemplos.

Caso 1

Empecemos con una película con el framerate a 1 segundo, con código en el primer fotograma que crea contenido gráfico. Supongamos que Flash Player tarda 200 milisegundos en procesar el código, y que tarda 200 milisegundos más en hacer una actualización de la pantalla. El proceso sería el siguiente:

Procesamiento de 1 frame a 1 fps

Así pues, la cabeza lectora alcanza el primer fotograma y empieza a ejecutar el código. Una vez que ha acabado la pantalla no se actualiza inmediatamente, ya que el refresco viene determinado por el framerate, que está puesto a un segundo. Se entra en un tiempo de espera. Cuando se cumple el ciclo del framerate se procede a actualizar la pantalla, acción que tampoco es inmediata. Una vez que el Flash Player ha conseguido dibujar todo el contenido por pantalla, salta a un nuevo frame y vuelve a empezar el proceso.

Caso 2

Ahora imaginemos que la porción de código del frame es considerable y el programa tarda 1500ms en realizar dicha ejecución. ¿Qué sucede cuando se cumple el ciclo del framerate y no se ha procesado todo el código? Pues que el renderizado no se efectuará, se pondrá a la cola hasta que el código haya sido procesado.

El runtime de Flash nunca interrumpe la ejecución de código para actualizar la pantalla.

Podemos demostrarlo con un sencillo ejemplo. Tenemos una película con 5 frames y un framerate de 1 segundo. El quinto fotograma tiene un script que, en función de si está o no activo el checkbox, genera un bucle 9 mil millones de veces con el único objetivo de ralentizar el procesador.

This movie requires Flash Player 9

Descargar ejemplo

Si el checkbox está activado al entrar en el frame 5, Flash Player comienza a ejecutar código que no puede procesar antes del ciclo del framerate, por lo que la pantalla no se actualiza y vemos durante más tiempo el número “4”.

Nota: cada swf tiene un tiempo máximo para ejecutar un script. Si se supera, aparece el famoso mensaje de “Un script de esta película está provocando que el Reproductor de Flash se ejecute lentamente“.

En AS3 está documentado como el error #1502: “El tiempo de ejecución del script ha superado el tiempo de espera predeterminado de 15 segundos“.
El valor del tiempo se puede configurar en las propiedades de publicación.

Caso 3

Pasemos ahora a una película con un sólo frame (hay que recordar que aunque sólo haya un frame o esté pausado, a efectos del runtime es como si hubiera una linea de tiempo infinita). En este frame se añade un listener al stage para escuchar un evento MouseEvent.CLICK, y con cada click se pinta una redonda en la posición del puntero. El framerate del swf está puesto a 0.4 (dos segundos y medio). Es notable la diferencia de tiempo desde que se ejecuta el código hasta que se dibujan las redondas en pantalla.

This movie requires Flash Player 9

Descargar ejemplo

Si pensamos en el gráfico del “caso 1” sabemos que el código que registra el listener se ejecuta al principio, y que a cada ciclo se renderiza la pantalla. Pero… ¿cuándo se ejecuta el código asociado al evento click? Veamos otro gráfico más detallado.

Procesamiento de 1 frame a 1 fps

Cuando se entra en “Tiempo de espera”, cualquier código generado como resultado de un evento (de mouse, de teclado, de un Loader, …) se ejecuta en este periodo y se muestra en pantalla en el siguiente ciclo.

Debido al exagerado framerate utilizado en este ejemplo, en cada frame se puede generar varias veces código para pintar los puntos, y en “Tiempo de renderizado” se actualizan todas a la vez.

Caso 4

Añadamos un simple botón al ejemplo anterior. Probemos a hacer algunos clicks para dibujar puntos y acto seguido rollover sobre el botón.

This movie requires Flash Player 9

Descargar ejemplo

Curiosamente, al hacer rollover se dibujan inmediatamente los puntos, aunque no hayan pasado los 2’5 segundos del framerate. Esto es así porque ciertos eventos pueden forzar al Flash Player a renderizar la pantalla. Como en este caso, cada vez que se interactúa con un botón se indica al runtime que debe actualizar la pantalla para reflejar el cambio de estado del mismo. Esta actualización no afecta únicamente al objeto, sino a todos los que están esperando para ser procesados.

Al margen de los botones, acciones como pasar el puntero sobre un Sprite o usar el tabulador entre cajas de texto también fuerzan a renderizar.

Y aún hay más, porque mediante el método updateAfterEvent, que muchos recordaréis de versiones anteriores de ActionScript, podemos indicar al Player que actualice la pantalla. En AS3 se puede invocar en las siguientes clases: MouseEvent, KeyboardEvent y TimerEvent.

En el ejemplo anterior, sin modificar el framerate, añadamos un updateAfterEvent al evento click y veremos como se pintan los puntos sin demora.

This movie requires Flash Player 9

Descargar ejemplo

Así pues, podemos hablar de dos categorías a la hora de renderizar:

  • La que se genera a intervalos regulados por el framerate
  • La que se genera bajo demanda de ciertos eventos

Al principio del artículo comentaba que el sistema de actualización de pantalla de Flash Player era automatizado. El hecho de poder usar updateAfterEvent no cambia nada, ya que realmente este método no actualiza la pantalla directamente, sólo indica al runtime la necesidad de hacerlo. Sin en el momento de la petición se encontrara ejecutando código no lo interrumpiría.
Es importante recordar que el renderizado siempre está gobernado internamente.

Caso 5

Veamos ahora un último ejemplo para entender como encaja el evento enterFrame en este rompecabezas.

En AS3 se puede definir un evento enterFrame en cualquier objeto del tipo display object (se encuentre o no en la displayList) y obviamente está sincronizado con el framerate de la película. Una vez definido, este evento se dispara cuando la cabeza lectora alcanza un nuevo fotograma, y lo hace antes de ejecutar cualquier código.

Procesamiento de 1 frame a 1 fps con un evento EnterFrame

Así pues, se alcanza un nuevo frame, se dispara un Event.ENTER_FRAME y se ejecuta el código asociado, acto seguido se ejecuta el código del frame y finalmente se entra en tiempo de espera hasta el siguiente renderizado.

Es importante notar que una vez que se dispara un Event.ENTER_FRAME lo hace a todos los objetos que están suscritos, aunque estén en diferentes líneas de tiempo, y se ejecuta siempre el código asociado de todos antes que el código del frame.

Para ejemplificar lo comentado, dejo un fla que define varios enterFrame en diferentes momentos y con código en cada frame. En la ventana de output se puede ver el orden de ejecución.

Descargar ejemplo

Comprobación de renderizado

En los gráficos que hemos visto, una vez pasado el “tiempo de espera” se entraba directamente en el “tiempo de renderizado”. Esto no es exactamente así.

Al finalizar el tiempo de espera se evalúa si ha habido cambios “visuales” desde el último frame. Si hay nuevos datos que dibujar o modificar, se procede a actualizar la pantalla, pero si el contenido del último frame y del actual son visualmente idénticos no se entra en “tiempo de renderizado”. De esta manera, el runtime evita hacer operaciones innecesarias.

Además, Flash Player sólo renderiza las zonas sensibles al cambio, no hace un update de toda la pantalla. Al conjunto de las zonas que hay que pintar se le llama “regiones de dibujo”, conocido en inglés como “redraw region”. Si miramos en el menú contextual cuando ejecutamos una película en el IDE (o en una versión debug), veremos la siguiente opción:

y al activarla encontraremos una serie de rectángulo rojos, que son las zonas que el Flash Player ha dibujado en el último render.

Framerate real

Durante todo el artículo hemos dicho varias veces que el framerate marca los ciclos en los que hay que actualizar la pantalla. Aunque esto es verdad, el framerate que le indicamos a nuestra película no suele alcanzarse la mayoría de veces, ya que en función de factores internos (la complejidad del código a ejecutar) y externos (la potencia de la máquina en la que se ejecuta Flash), el número de frames por segundo será menor al indicado. La cantidad de frames que es capaz de procesar el Flash Player en un segundo se conoce como framerate real.

Para mis experimentos utilizo una clase muy simple que cuenta los frames reales que se procesan en un segundo y los muestra por pantalla. Un ejemplo de su uso:

This movie requires Flash Player 9

Descargar ejemplo

El ejemplo está puesto a 31 fps. Lo primero que hay que tener en cuenta es que por el hecho de correr una película en un navegador ya pierde 4 o 5 frames. A medida que vayamos pintando el framerate real cada vez irá bajando más y más, debido a la carga de procesamiento. Hay que advertir que si dejamos de interactuar, el framerate se recupera aproximadamente a los valores iniciales, ya que en ese momento no se procesa código y debido a que no hay cambios visuales el Flash Player no pasa por el proceso de renderizado.

Nota: la clase FPS da un valor aproximado de la cantidad de fps. Se debe utilizar únicamente para depurar ya que no limpia sus recursos.

Para acabar, veamos un resumen con todos los puntos claves del artículo.

Resumiendo

Flash Player cuenta con un sistema basado en frames para procesar código, y éste se combina con un motor de renderizado para dibujar los gráficos en pantalla.

La frecuencia de dicho procesamiento viene marcada por el framerate del swf.

El proceso de renderizado es automatizado.

Flash Player nunca interrumpe la ejecución de código para actualizar la pantalla.

Hay dos tipos de situaciones que desencadenan el renderizado: la que se genera a intervalos regulares marcados por el framerate y la que se genera tras la petición de ciertos eventos.

Cada vez que se alcanza un frame se sigue este proceso:

  • Se dispara un evento ENTER_FRAME en caso de estar definido.
  • Se ejecuta en código asociado al enterFrame.
  • Se ejecuta el código del frame actual.
  • Se entra en un modo de espera hasta que toque renderizar la pantalla.
  • Si durante la ejecución de código anterior se ha definido eventos, se recogen y procesan en este tiempo de espera.
  • Una vez cumplido el ciclo del framerate se procede a una comprobación: si ha habido cambios gráficos se renderiza, sino, se salta al siguiente frame.

Una vez iniciada una película, el Flash Player la trata como si tuviera una linea de tiempo infinita, no se deja de ejecutar hasta que se descarga.

El framerate asignado a una película no suele cumplirse, siempre suele ser menor debido a factores como la cantidad de código a ejecutar, la complejidad de éste, la potencia del ordenador, los recursos del sistema operativo, etc.

Información del artículo

Post publicado el 24 de May de 2008 a las 17:56 por llops

Categorias: Artículos

Etiquetas: , , , , , , ,

Comparte

2 trackbacks

15 comentarios

  • Zah

    Muy buen post, ¡felicidades!. Me ha parecido interesante y bien explicado.

    Por cierto, en el primer ejemplo, no podía desseleccionar el checkbox, y acabó haciéndome tener que actualizar la página T_T.

    Con todo, como digo, ha merecido la pena leerlo con atención.

  • Ey Zah, gracias por tus palabras!

    Y en cuanto al ejemplo del checkbox… quizá me pasé con el loop :P

  • Gran post, si señor. Este tipo de explicaciones, muy gráficas, sencillas y directas al grano, pueden ayudarnos a conocer con más profundidad el producto con el que trabajamos y a mejorar el rendimiento y, por ende, la experiencia de nuestras aplicaciones.

    Desde ya, pasará a mi biblioteca ^^.

  • Hola fiera, muy buen post, si señor, y que conste que yo también te leo, y desde hace tiempo. Me gusta como explicas las cosas de claritas, cosa que no abunda mucho en español y sobre AS. Tengo amigos que opinan lo mismo de ti.

  • Yasan

    Muy bueno la ponencia del render, muy ilustrativo como el player se comporta ante determinada acciones y que eventos ayudan a acelerar este proceso, el funcionamiento del framerate y cual es el orden por el que se ejecuta el código, de verdad muchas gracias por compartir estos conociemientos con todos, sigue asi, y estaremos a la expectativa del siguente post, que de seguro será tan bueno como este.

    Saludos desde Lima

  • Muy buena esta serie la que estás escribiendo sobre actionscript!

    Gracias.

  • Nunca había encontrado un post que explicara tan claramente algo tan básico a la hora de trabajar con AS.

    Hacia tiempo que me rondaba por la cabeza come ejecutaba exactamente las acciones el FlashPlayer.

    Muy bueno si señor.

  • Por cierto llops, tus rss hacen cosas extrañas, en algunos posts sale un listado de enlaces en lugar de mostrar el contenido, que no se ve hasta que entras en el post.
    Un saludo

  • Ken

    El manejo de esta teoria es imprescindible al momento de ejecutar codigos que barren contra un evento, y sobre todo si uno necesita que se ejecuten en u orden y otro. Conociendo esto nos permite tomar medidas para manejar mejor el orden de ejecucion de nuestro codigo … que bueno que lo hayas hecho tan simple y sin perderle la esencia. =D

  • Beatzoo

    Brutal.

  • Tutan

    Muy buen artículo, excelente y se ve claramente que te tomaste tu tiempo para prepararlo con sus imágenes :D
    Solo me gustaría hacer una acotación y es que ENTER_FRAME siempre se dispara. Seguramente te refieres a si existiera o no un listener escuchando.
    Un saludo
    Tutan

  • He visto la luz! al fin entiendo bien como funciona esto, estoy desarrollando un juego en flash para Facebook y tenia problemas de fluidez de gráficos ahora ya se como solucionarlo. Muchas Gracias. Saludos!

  • Chepe

    Hola! Muy buen articulo. Te felicito. No tengo experiencia con este tipo de programación (que no es linear, digamos haci), por eso, algunas cosas no las entiendo y consequentemente tengo una duda:

    1 – Si mi código tiene 50, 200 ou 600 lineas, por ejemplo, todas estas lineas serán ejecutadas en el Tiempo de ejecución sin interrupción?

    Otro punto que na comprendi, que no tiene que ver con programación es: framerate puesto a 0.4 equivale a 2 segundos y medio.

    Muchas gracias por tu atencón y la paciencia.

  • Hola Chepe, te contesto:
    1 – Sí. Si las líneas están pendientes de ejecución (en la cola), hasta que no se ejecuta la última no se pasa al siguiente frame. Si la película está necesitando mucho tiempo para ejecutarse pasa lo que menciono en la “Nota” del “Caso 2”.
    2- Simplemente es la división de 1/0.4 = 2.5
    Si un 1fps es igual a 1 segundo, 0.5fps es igual a 2 segundos, 0.4 igual 2.5, etc.

    Por cierto, gracias a todos por los comentarios. Hacía tiempo que no pasaba por aquí y me alegra ver que el artículo ha resultado útil tiempo después :)

  • Excelente y refrescante encontrarse con este tipo de artículos y con metodología orientada al que desea aprender. Muchas gracias.